Agenda
- resources
- statistical modeling foundations
- Samples & populations
- Sample statistics
- Bootstrapping
- Outliers
- Regression
Announcements
- Keep up with Piazza
- Canvas assignments
- MDSR Chap 5 Exercises
- Mini-Project: Bike Share and Local Weather
- Programming Notebooks: MDSR Chapter 07
- Your results may differ from the book in Sec 7.2 & Sec 7.3 (it’s OK)
- likely
set.seed()
issue, so random/simulated processes differ
Statistical Foundations
- Goal: extract meaning from data
- Common pitfall…
- don’t underestimate importance of visualization
- don’t overestimate importance of analytical results
- both have important and complimentary roles
Key ideas: Samples, populations, sampling distributions
- our data are just a sample representative of some larger population (or process)
- the sample is random
- if another person were to replicate the study, they would get a different sample
- if we were to do the study again in the future, we would get a different sample
- we care about patterns/structure/models of the population
- a good model includes salient features of the population to result in similar outcomes across (random) samples
- the sampling distribution is simply a distribution of plausible outcomes that would be observed if the study were repeated infinitely many times.
- basically, this is a hypothetical distribution of a sample statistic among all possible samples of a given size
- this primarily gives us a sense of the variability to be expected from one sample to another
Statistical Foundations
- we often don’t actually care that much about the specific sample that we have
- a sample is a one-time realization of some data collection process
- if we repeat the study/investigation and collect new data we almost certainly observe a different sample
- if we can’t make conclusions beyond the specific sample we happened to encounter, we typically haven’t gained much
- we typically care far more about what the sample represents (and it’s limitations)
- exploration or explanation of some larger population or process
- outcomes/relationships in the data that can be replicated/reproduced
- future predictions
- and carefully quantifying uncertainty of the outcomes above
Example: meet Chris

- Motivation:
- A close friend of mine is an executive management consultant with PwC
- Chris lives on the East Coast, but travels (M-Th) every week for work
- His primary clients (right now) are on West Coast & Midwest.
- Flight delays are a major threat, because if the client can’t rely on him to be there on time they may give the project to a different consultant.
- Goal: He wants to maximize time at home with his wife and kids, without jeopardizing work commitments
- How early should Chris should plan to arrive in Chicago (ORD) if he wants to avoid missing meetings due to flight delays?
- Q: Ideas how to approach the problem?
r
r require(tidyverse) require(mdsr) require(mosaic) require(nycflights13) data(flights) # Get flights from NYC to ORD OrdDelays <- flights %>% filter(dest == )
Example: flight delays from New York to Chicago
r
r OrdDelays28 <- OrdDelays %>% sample_n(size = 28)
- Chris flies a lot and knows some statistics, but lets suppose he only has data from 28 flights
- Here are some summary statistics
- Q: How should he decide when to arrive?
r
r # summary statistics of delays favstats( ~ arr_delay, data = OrdDelays28)
Example: flight delays from New York to Chicago
- Trade-off… Maybe Chris can risk being late once in a while to have more time with his family
- Here’s what he might decide based on his sample
- he flies just about every week
- 97% percent on-time would mean that he expects to be late a little more than once per year.
r
r tolerance28 <- qdata(~ arr_delay, p = 0.97, data = OrdDelays28) tolerance28
p quantile
0.97 103.16
Example: flight delays from New York to Chicago
- Chris doesn’t have the population
- he can’t know if his is an effective policy or not…
- does this really work 97% of the time??
- Is he too late? Too early??
- In our hypothetical exercise, we do have a population,
r
r # reality of population (unknown/unknowable to Chris) favstats( ~ arr_delay, data = OrdDelays) # full data
r # population value for 0.97 quantile tolerance <- qdata(~ arr_delay, p = 0.97, data = OrdDelays) # full data tolerance
p quantile
0.97 132.00
r
r # Actual performance for Chris… tally(~ arr_delay < tolerance28[2], data = OrdDelays, format = )
arr_delay < tolerance28[2]
TRUE FALSE <NA>
0.91511890 0.04339524 0.04148585
Problem solved? (Not quite)
- There’s still quite a bit of risk due to randomness…
- an interval estimate for the 0.97 quantile would be better
- Q: how should we do it?
r
r # Results vary… four different samples = four different results n <- 28 qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = n, replace = TRUE))
p quantile
0.97 83.54
r
r qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = n, replace = TRUE))
p quantile
0.97 45.28
r
r qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = n, replace = TRUE))
p quantile
0.97 88.86
r
r qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = n, replace = TRUE))
p quantile
0.97 59.12
Distribution of the estimate depends on sample size
- Let’s simulate 1000 samples from our
OrdDelay
(full) data
- Any one of these could be the sample that Chris happened to observe
- We just want to see how much these samples vary from one to the next
r
r n <- 28 SimsSmallN <- mosaic::do(1000) * qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = n, replace = TRUE)) # inspect result head(SimsSmallN)
r # variability of our 0.97 quantile estimate (note ) favstats(~ quantile, data = SimsSmallN)
Vocabulary
- Sample size (n) is the number of cases in an observed sample
- Sampling distribution is the collection of the sample statistic from all trials
- a theoretical sampling distribution includes all possible trials
- we have 1000 simulated trials,
- that specific number doesn’t matter much as long as it’s “large”
- 10s of thousands or millions are common in practice
- do more if a precise result is important
- shape of the sampling distribution is worth noting (ours is right-skewed here)
- standard error is the standard deviation of the sampling distribution for a statistic
- Here, we estimate the SE using the SD of many simulated 0.97 quantile estimates
- you can find this result in our
favstats()
output above
r
r SimsSmallN %>% ggplot() + geom_histogram(aes(x = quantile)) + ggtitle(\1000 simulated 0.97 quantiles for flight delays among samples of n = 28)

What if we have a bigger sample?
- Maybe Chris can pool flight information with a few colleagues that regularly fly NYC ro ORD
- No flight counted more than once (i.e. if they had shared a flight)
- Q: Would a bigger data set matter? What do you expect to change?
r
r nBigger <- 150 SimsBigN <- mosaic::do(1000) * qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = nBigger, replace = TRUE)) # inspect result head(SimsSmallN)
Plot comparison (n = 28; n = 150)
r
r # combine results into a single tbl BothSims <- bind_rows(SimsSmallN %>% mutate(SampleSize = n), SimsBigN %>% mutate(SampleSize = nBigger)) # summary comparison favstats(quantile ~ SampleSize, data = BothSims)
r # plot comparison BothSims %>% ggplot(aes(x = quantile)) + geom_histogram(bins = 30) + facet_grid( ~ SampleSize) + xlab(0.98 Quantile)

Access to Sampling Distribution
- Important: in the previous exercise we were able to simulate an actual sampling distribution because we were actually drawing random samples from the population.
- We nearly always just have one sample
- We need another way to access the sampling distribution
- With just one sample of the data we have a few options to characterize a sampling distribution
- Analytical solutions derived from statistical theory (e.g. t-test)
- Simulation-based methods
Bootstrapping
- We assume our sample is representative of the population
- The population can be reasonably approximated by many, many, many copies of our sample
- Just as we had previously drawn many samples from the population, “bootstrapping” approximates this process by drawing simulated samples (with replacement) from our original sample
Discussion Questions:
- Why do we need to sample with replacement?
- Does bootstrapping collect new data?
- Do you expect the mean of the bootstrap sampling distribution to be equivalent to the mean of the (true) sampling distribution?
- Do you expect the standard error of the bootstrap sampling distribution to be equivalent to the standard error of the (true) sampling distribution?
Back to Chris…
Chris wants to be more confident that flight delays will not make him late more than 3% of the time, what should he do?
- He pools flight delay data with colleagues and they come up with a much bigger data set of 230 flights - We’ll call it OrdColleagueData
Bootstrap confidence interval
- using the data combined among Chris’ colleagues–
OrdColleagueData
–we sample with replacement and calculate the 0.97 quantile each time.
- we now have a distribution of bootstrap sample quantile estimates (1000 of them in this case)
- we use that distribution to produce an interval estimate
- In this case, a confidence interval for 97th percentile of arrival delay
bootstrap sampling distribution
r
r # Chris and colleagues pool data for 230 samples OrdColleagueData <- OrdDelays %>% sample_n(size = 230, replace = FALSE) # bootstrap distribution BootStrapTrials <- mosaic::do(1000) * qdata(~ arr_delay, p = 0.97, data = sample_n(OrdColleagueData, size = 230, replace = TRUE))
confidence interval
- If the bootstrap sampling distribution looks approximately Normal:
- compute the Chris’s sample mean
- estimate the standard error using the standard deviation of the bootstrap sampling distribution
- Confidence interval: \(est \pm z^* (SE)\)
- “est” is the estimate from the sample (Chris’ data)
- \(z^*\) is a critical value from the Normal distribution commensurate with confidence level (e.g. 1.96 for 95% CI)
- \(SE\) is the standard error which we’ve estimated from the standard deviation of the bootstrap distribution
- If bootstrap sampling distribution is fairly smooth, but not symmetric (e.g. skewed),
- we could use a percentile method to approximate our confidence interval
- not perfect, but still useful
- If bootstrap sampling distirbution has big gaps or other problems,
- we should think hard about alternative solutions…
- e.g., what’s making the sampling distribution so ugly?
Our results?
- the distribution is not pretty… it’s sort of a borderline case
- definitely not Normal, but possibly still useful
- we should even be suspicious of a bootsrap percentile interval
- we’ll suppose he wants to 80% confident his flights will arrive on time 97% of the time
- Discussion questions
- Q: How should Chris decide how early he should plan to arrive?
- Q: Why could make this bootstrap sampling distribution look so strange?
r
r BootStrapTrials %>% ggplot() + geom_histogram(aes(x = quantile))

r
r # Maybe Chris just wants to be 80% confident he will be on time 97% of the time qdata(~ quantile, p = 0.8, data = BootStrapTrials)
p quantile
0.8 105.2
Outliers
- Part of the problem for Chris was the fact that
- we were interested in characterizing the tail of the disribution;
- we had a lot of extreme observations (potential outliers)
- An outlier is an observation that doesn’t seem to conform to the pattern of the data
- completely legitimate observations may appear to be outliers
- problematic outliers are cases that are fundamentally different from the population
- We can learn a lot by studying extreme observations in the data
Long delays
- The longest delays (> 6 hrs) were most often associated with American Eagle (MQ) and United Airlines (UA) and frequently occur in the Spring months (March, April, May).
- Q: What should we do with this information?
r
r OrdDelays %>% filter(arr_delay >= 360) %>% transmute(carrier, month, day, dep_delay, arr_delay, air_delay = arr_delay - dep_delay) %>% arrange(desc(arr_delay))
132 minute delays
- Actually, American Eagle (MQ) isn’t so bad after looking at carrier volume & comparing with Chris’ threshold
- Best carrier performance
- American Airlines looks better than average
- United meets expectation;
- Below average carrier performance
- Pinnacle Airlines (9E)
- JetBlue (B6)
r
r # inspect long delay volume OrdDelays %>% filter(!is.na(arr_delay)) %>% mutate(long_delay = arr_delay >= 132) %>% tally( ~ long_delay | carrier, data = ., format = , margins = TRUE)
carrier
long_delay 9E AA B6 EV MQ OO UA
TRUE 56 130 44 0 74 0 194
FALSE 928 5716 848 2 2023 1 6550
Total 984 5846 892 2 2097 1 6744
r
r # inspect long delay proportion OrdDelays %>% filter(!is.na(arr_delay)) %>% mutate(long_delay = arr_delay >= 132) %>% tally( ~ long_delay | carrier, data = ., format = ) %>% round(2)
carrier
long_delay 9E AA B6 EV MQ OO UA
TRUE 5.69 2.22 4.93 0.00 3.53 0.00 2.88
FALSE 94.31 97.78 95.07 100.00 96.47 100.00 97.12
We need a way to account for other variables in our model…
- Q: Are the effects we’re observing real or are we just being fooled by randomness?
- Q: How should we evaluate?
- interpret
- conclude
- what about more data?
r
r # for simplicity, we model average arrival delays here ordModel <- OrdColleagueData %>% mutate(carrierAA = if_else(carrier %in% c(), true = , false = ), season = if_else(month %in% 3:5, true = , false = )) %>% lm(arr_delay ~ carrierAA + season, data = .) # How to interpret? What have we learned? msummary(ordModel)
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.1045 4.5112 0.023 0.982
carrierAAOtherCarrier 3.9967 5.2874 0.756 0.451
seasonSpring -1.3640 6.1857 -0.221 0.826
Residual standard error: 35.98 on 215 degrees of freedom
(12 observations deleted due to missingness)
Multiple R-squared: 0.002808, Adjusted R-squared: -0.006469
F-statistic: 0.3027 on 2 and 215 DF, p-value: 0.7392
r
r confint(ordModel)
2.5 % 97.5 %
(Intercept) -8.787304 8.996376
carrierAAOtherCarrier -6.425081 14.418442
seasonSpring -13.556301 10.828320
Segue to Model Basics
- We were trying to use Chris’ flight data to come up with a rule that could be useful for preventing excessive delays from jeopardizing his meetings in Chicago
- We started by only looking at the arrival delay itself
arr_delay
- After poking around for a while, we noticed that useful information might be gained from other variables
- carrier
- time of year (longest delays in Spring)
Multiple Testing
- If you end up evaluating multiple tests of the same data, you lose control of Type I error rate
- Q: What does type I error rate mean?
- Q: How do we quantify type I error rate?
- Q: What’s the big deal? Why does it matter??
- Multiple testing and appropriate adjustments
r
r # 3 Tests… 1 - (1 - 0.05)^3
[1] 0.142625
r
r # Bonferroni adjustment for 3 tests b <- 0.05 / 3 1 - (1 - b)^3
[1] 0.0491713
ASA Statement on p-values…
- Lots of people have heard of “p-values” and have some vague concept of “smaller the better”
- Incomplete understanding can lead to bogus conclusions and distrust of statisticians & data analysts
- Here’s what the American Statistical Assoc wants everyone to know about p-values
- p-values can indicate how incompatible the data are with a specified model
- p-values do NOT measure the probability that the studied hypothesis is true, or the probability that the data were produced by random chance alone
- scientific conclusions and business or policy decisions should NOT be based only on whether a p-value passes a specific threshold
- proper inference requires full reporting and transparency
- a p-value, or statistical significance, does not measure the size of an effect or the importance of a result
- by itself, a p-value does not provide a good measure of evidence regarding a model hypothesis.
Hypothesis generation vs. hypothesis confirmation
- traditional focus of modelling is on inference–attempting to confirm some hypothesis is true (or not false…)
- the process is not complicated, but can be hard because we need to understand that
- Each observation can either be used for exploration or confirmation, not both.
- You can use an observation as many times as you like for exploration, but only once for confirmation.
- As soon as you use an observation twice, you’ve switched from confirmation to exploration. If possible, consider partitioning the data as follows:
- 60% of the data for exploration (or training) set
- 20% of the data for a query (or validation) set
- 20% of the data for a test (or confirmation) set
Model Basics
Goals of modeling:
- “provide a simple, low-dimensional summary of a data set” (R4DS)
- You may use these tools in different contexts for various purposes
- description
- inference
- prediction
- In each case, we are explaining variation
- some variation in the data is structural
- some variation in the data is simply randomness
Choosing a model
- Model family (general)
- Chosen based on understanding of data and goals of analysis
- Expresses a precise (but generic) pattern you want to capture
- Line: \(y = a_1 + a_2 * x\)
- Curve: \(y = a_1 * x^{a_2}\)
- \(y\) & \(x\) are variables that are observed in your data
- \(a_1\) & \(a_2\) are parameters that can be modified to capture different patterns
- Fitted model (specific)
- Constrained by model family
- Choose parameters that result in the model that is “closest” to your data
- Line: \(y = 7 + 3 * x\)
- Curve: \(y = 9 * x^2\)
All models are wrong (some are useful)
- “All models are wrong, some are useful” –George Box (statistician)
- Classical Mechanics (physics):
- Isaac Newton’s “laws” of motion (physics) are demonstrably wrong
- The model generally does well describing motion for objects as small as bacteria and as large as planets, stars, and galaxies.
- Newton’s laws were known to be inaccurate when predicting the orbit of Mercury
- Close enough?! (i.e. useful?)
- Einstein’s theory of (general) relativity
- Classical mechanics breaks down when bodies involved are extremely massive or moving extremely fast
- Accurate GPS requires relativity (Newton would be off by a couple miles)
Moral: all models are wrong; some are useful
Newtonian physics was known to be wrong for nearly 2 centuries before relativity came along, in part because it failed to accurately predict the orbit of Mercury. The model was (and is?) useful for tons of practical purposes
Einstein’s theory of relativity is almost certainly wrong too because it fails for subatomic particles… but it’s still useful
Simple example
- We’ll use linear regression as a sandbox to build some intuition and tools
- once you understand some basic principles in the context of regression, we can translate to other models
- consider a simulated data set
sim1
with variables y
and x
- it looks like they’re related to one another, so perhaps we can model their relationsip
- Family: \(y = b_0 + b_1 * x\)
- Fit: choose the “best” \(b_0\) and \(b_1\)
r
r sim1 %>% ggplot(aes(x = x, y = y)) + geom_point()

Simple example (random models)
- we could just randomly generate models and pick a few candidates that look “best”
- intuition says this is silly approach…
- Why do we think know better?
- How might we judge whether one model is “better” than another?
r
r models <- tibble( b0 = runif(250, -20, 40), b1 = runif(250, -5, 5) ) sim1 %>% ggplot(aes(x, y)) + geom_abline(aes(intercept = b0, slope = b1), data = models, alpha = 0.25) + geom_point()

Distance from points to the model (1/4)
- a natural thought is to evaluate the vertical distance from each point to a candidate model
- the point represents our observed response (from the data)
- the corresponding location on the model is a prediction
- model fitting applet: http://www.rossmanchance.com/applets/RegShuffle.htm
- Note: x-values are shifted slightly to avoid overplotting in the figure

Distance from points to the model (2/4)
- calculating the distance directly
- let’s turn our model into a function
r
r model1 <- function(a, data) { # purpose: calculate predictions for a given model # inputs: ### a: vector of coefficient estimates for linear model (e.g. intercept & slopes) ### data: vector of observed response data
a[1] + data$x * a[2] } # show model predictions model1(c(7, 1.5), sim1)
[1] 8.5 8.5 8.5 10.0 10.0 10.0 11.5 11.5 11.5 13.0 13.0 13.0 14.5 14.5 14.5 16.0 16.0 16.0
[19] 17.5 17.5 17.5 19.0 19.0 19.0 20.5 20.5 20.5 22.0 22.0 22.0
Distance from points to the model (3/4)
- calculating the distance directly
- let’s turn our model into a function
- measure collective distance from all points to the model
- Statisticians typically use the “root-mean-squared” deviation
r
r measure_distance <- function(mod, data) { # purpose: calculate distance from observed points to model predictions # inputs: ### mod: vector of parameter estimates for linear model (e.g. intercept & slope(s)) ### data: observed data
diff <- data$y - model1(mod, data) sqrt(mean(diff ^ 2)) } # show RMS deviation for the model measure_distance(c(7, 1.5), sim1)
[1] 2.665212
Distance from points to each candidate models (4/4)
- calculating the distance directly
- let’s turn our model into a function
- measure collective distance from all points to the model
- repeat for all 250 of our models using
purrr::map2()
r
r sim1_dist <- function(b0, b1) { # purpose: helper function because our distance function # expects the model as a numeric vector of length 2 # inputs: ### b0: intercept estimate ### b1: slope estimate ### sim1: data in environment measure_distance(c(b0, b1), sim1) } models <- models %>% mutate(dist = purrr::map2_dbl(b0, b1, .f = sim1_dist)) models
Progress…
- We now have a distance metric that quantifies performance of all 250 of our random models
- Now we can start to evaluate if some of them are useful
r
r sim1 %>% ggplot(aes(x, y)) + geom_abline(aes(intercept = b0, slope = b1), data = models, alpha = 0.25) + geom_point()

Best (random) models for sim1
data
- we have a metric
- let’s inspect our top 10 results according to our distance criterion
- brightest models fit the data “best”
- note all the obviously silly models are gone
- several decent candidate models left
- Q: How can we do even better?
r
r sim1 %>% ggplot(aes(x, y)) + geom_point(size = 2, colour = 30) + geom_abline(aes(intercept = b0, slope = b1, colour = -dist), data = filter(models, rank(dist) <= 10))

Best (random) models for sim1
data
- What if we visualized the slope (b1), intercept (b0), and distance metric (diff) on a 3-dimensional plot?
- slope and intercept are both related to distance metric
- we just want to search for the “best” combinations using our criterion
- points are slope/intercept pairs (i.e. models) colored by our distance criterion
- brighter color is “better” (i.e. less collective distance from model to data)
- the top 10 shown previously are highlighted in red
r
r ggplot(models, aes(b0, b1)) + geom_point(data = filter(models, rank(dist) <= 10), size = 4, colour = ) + geom_point(aes(colour = -dist))

Random models was silly… but not that silly
- Turns out we could learn quite a bit with this method, and came up with a handful of decent models
- Q: How might we extend the method for a more systematic approach?
Systematic search
- Maybe instead of searching randomly, we search systematically with a grid of slope-intercept combinations?
- we’ll start with 250 models again
- we can control how fine the grid is if we want
- avoid clumping and sparsity of randomness
- so far the new grid suggests
- b0 is maybe around 4
- b1 is maybe around 2
- Q: Is the model \(y = 4 + 2X\) wrong? Is it useful?
r
r # create systematic grid; fit all models to the data grid <- expand.grid(b0 = seq(-5, 20, length = 25), b1 = seq(1, 3, length = 25)) %>% mutate(dist = purrr::map2_dbl(b0, b1, sim1_dist)) # plot models as points; color
grid %>% ggplot(aes(b0, b1)) + geom_point(data = filter(grid, rank(dist) <= 10), size = 4, colour = ) + geom_point(aes(colour = -dist))

r
r sim1 %>% ggplot(aes(x, y)) + geom_point(size = 2, colour = 30) + geom_abline(aes(intercept = b0, slope = b1, colour = -dist), data = filter(grid, rank(dist) <= 10))

Systematic search cont’d
- let’s try a finer grid…
- best results so far
- b0 between 0 and 7;
- b1 between 1.5 and 2.5
- Q: could we just continue this way?
r
r # create systematic grid; fit all models to the data grid <- expand.grid(b0 = seq(0, 7, length = 50), b1 = seq(1.5, 2.5, length = 50)) %>% mutate(dist = purrr::map2_dbl(b0, b1, sim1_dist)) # plot models as points; color
grid %>% ggplot(aes(b0, b1)) + geom_point(data = filter(grid, rank(dist) <= 10), size = 4, colour = ) + geom_point(aes(colour = -dist))

r
r sim1 %>% ggplot(aes(x, y)) + geom_point(size = 2, colour = 30) + geom_abline(aes(intercept = b0, slope = b1, colour = -dist), data = filter(grid, rank(dist) <= 10))

Recap of our grid search…
- We defined a distance metric (root-mean-square deviation) that we want to use to compare models
- Smaller RMS deviation indicates the model is “close” to the data
- The “best” model is the one that minimizes our criterian (i.e. RMS deviation)
- Search process:
- decided on a model family (linear in this case)
- We started with a bunch of random lines, but then decided a systematic grid of combinations was a better idea
- We picked the 10 best models using our criterion
- We then searched again with a finer grid
- Repeat steps 4 & 5 until satisfied
- Estimate model parameters
optim()
and the Newton-Raphson alrogithm
- The Newton-Raphson algorithm is a numerical optimization search
- pick a starting point for your parameter estimates
- look around for steepest slope (i.e. greatest improvement against our metric)
- ski down that slope a bit
- repeat
- you’re done when you can’t get any lower
- the
optim()
function does something conceptually similar for us
- we provide starting point:
c(0, 0)
- we provide function to define distance between model and data:
measure_distance
- we provide data:
sim1
- R does the rest (note: our grid search wasn’t so bad!!)
- Q: how might this algorithm possibly result in a misleading conclusion?
- This doesn’t usually happen, but you need to know it’s a risk
- Q: Ideas to protect against this risk?
r
r nrBest <- optim(par = c(0, 0), fn = measure_distance, data = sim1) # show parameter estimates nrBest$par
[1] 4.222248 2.051204
lm()
for linear models
- We had chosen “linear model” as our model family
- The
lm()
function in R is very efficient for the linear model family
- family: \(y = b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ...\)
- R syntax:
lm(Y ~ X1 + X2 + X3, data = DataSet)
lm()
even fixes our problem with optim()
to guarantee delivery of global minimum
- Q: thoughts about our three approaches?
- random/grid search
- Newton-Raphson with
optim()
lm()
- (having fun? take STAT 440!)
r
r sim1_lm <- lm(y ~ x, data = sim1) sim1_lm$coefficients
(Intercept) x
4.220822 2.051533
Quantifying uncertainty of our estimates
- We now have point estimates, but that does little for us unless we understand the uncertainty of those estimates (e.g. standard error)
lm()
has this functionality built in for us
confint()
is also very useful for interval estimates
- Q: how could we achieve something similar using
optim()
?
r
r # confidence intervals confint(lm(y ~ 1, data = sim1)) # what does this do?
2.5 % 97.5 %
(Intercept) 13.12483 17.88368
r
r confint(sim1_lm, level = 0.95) # what does this do?
2.5 % 97.5 %
(Intercept) 2.441112 6.000532
x 1.764707 2.338359
r
r # model summary table msummary(sim1_lm)
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4.2208 0.8688 4.858 4.09e-05 ***
x 2.0515 0.1400 14.651 1.17e-14 ***
Residual standard error: 2.203 on 28 degrees of freedom
Multiple R-squared: 0.8846, Adjusted R-squared: 0.8805
F-statistic: 214.7 on 1 and 28 DF, p-value: 1.173e-14
Quantifying uncertainty of our estimates
- Multiple approaches…
- bootstrapping
- sample with replacement from our data
- compute the estimates for each bootstrap sample
- calculate standard deviation of our bootstrap estimates to approximate the standard error
lm()
takes advantage of some nice mathematical approximations
- IF the data conform to the assumptions required for those mathematical approximations,
lm()
is convenient, accurate, and commonly used
- the bootstrap should deliver similar results, though
- IF the data do NOT conform to the assumptions,
- you may need some more advanced skills to use
lm()
responsibly (take STAT 462)
- you should think about a different model family
- bootstrapping may or may not be useful depending on the issues at stake
r
r nrBest <- optim(par = c(0, 0), fn = measure_distance, data = sim1) nrBest$par
[1] 4.222248 2.051204
r
r # note the $par
at end of optim()
… try without to see why BootstrapModels <- mosaic::do(1000) * optim(par = c(0, 0), fn = measure_distance, data = sample_n(sim1, size = nrow(sim1), replace = TRUE))$par head(BootstrapModels)
r # standard error sd(~ V1, data = BootstrapModels) # intercept
[1] 0.9533629
r
r sd(~ V2, data = BootstrapModels) # slope
[1] 0.1490552
r
r # 95% CI (percentile method) qdata(~ V1, p = c(0.025, 0.975), data = BootstrapModels) # intercept
r
r qdata(~ V2, p = c(0.025, 0.975), data = BootstrapModels) # slope
Compare lm()
result to Bootstrap
- Note Standard Error estimates for each coefficient
- compare confidence interval estimates
- Q: what if we run the simulation again?
- Q: what if we run more bootstrap models?
r
r # recall our lm()
model for comparison msummary(sim1_lm)
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4.2208 0.8688 4.858 4.09e-05 ***
x 2.0515 0.1400 14.651 1.17e-14 ***
Residual standard error: 2.203 on 28 degrees of freedom
Multiple R-squared: 0.8846, Adjusted R-squared: 0.8805
F-statistic: 214.7 on 1 and 28 DF, p-value: 1.173e-14
r
r confint(sim1_lm)
2.5 % 97.5 %
(Intercept) 2.441112 6.000532
x 1.764707 2.338359
Exercise: Mean Absolute Deviation
- One weakness we are tolerating is a sensitivity to extreme observations
- Consider Mean Absolute Deviation vs. Root-Mean-Square Deviation
- Q: Compare & contrast the two approaches
- Q: How could you fit a model that minimizes the MAD metric?
- use
sim1
data from modelr
package?
Evaluating model fit
- Our model essentially partitions the information (i.e. variability) in the data into two parts
- structure that IS explained by the model (predictions)
- randomness that IS NOT explained by the model (residuals or “errors”)
- Note: The popular R-squared statistic is a simple description of this partition
Model predictions
- Regression could be described as “conditional expectation”
- Let’s consider sketching out our expectations, under various conditions…
- We want to use our model to calculated the expected value of Y at a bunch of different values of X
- We need a sequence of X values for our investigation using
modelr::data_grid()
- We’ll then calculate the expected value of Y (i.e. prediction) at each one using
modelr::add_predictions()
Initialize the grid
r
r grid <- sim1 %>% data_grid(x) # just a bunch of evenly spaced hypothetical values for X… head(grid)
Add some predictions using our existing model (sim1_lm
)
r
r grid <- grid %>% add_predictions(sim1_lm) # predictions accompanying each hypothetical X value in our grid head(grid)
Plot it!
- Admittedly, this isn’t the most direct way to add a regression line to a plot (e.g.,
geom_abline()
)
- The benefit: this simple approach extends to just about ANY model in R (even complex models)
- Your primary limitation now is visualization skills (NOT model complexity)
r
r # just the data ggplot(sim1, aes(x)) + geom_point(aes(y = y))

r
r # add the grid of predictions to the data ggplot(sim1, aes(x)) + geom_point(aes(y = y)) + geom_point(aes(y = pred), data = grid, colour = , size = 3)

r
r # since the model family is linear… ggplot(sim1, aes(x)) + geom_point(aes(y = y)) + geom_line(aes(y = pred), data = grid, colour = , size = 1)

Residuals
- predictions illustrate the variability in the data captured by the model
- residuals tell you what the model couldn’t capture/explain

Residuals
- predictions illustrate the variability in the data captured by the model
- residuals tell you what the model couldn’t capture/explain
- Q: what do we learn from this plot?
r
r sim1 <- sim1 %>% add_predictions(sim1_lm) %>% add_residuals(sim1_lm) # we’ve just appended the model residuals to our data set head(sim1)
r # Let’s look at the distribution of residuals sim1 %>% ggplot(aes(resid)) + geom_histogram(binwidth = 0.5)

r
r # or commonly with a density plot sim1 %>% ggplot(aes(resid)) + geom_density()

Residuals vs X (our predictor variable)
- Q: What do we learn here?
r
r sim1 %>% ggplot(aes(x, resid)) + geom_hline(yintercept = 0, linetype = 2) + geom_point()

Modeling categorical variables
- consider a new toy data set called
sim2
- inspect structure & variables
- plot the data
- fit model with
lm()
- generate predictions
- Q: why are there 4 predictions, and not 40?
r
r # inspect data structures str(sim2)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 40 obs. of 2 variables:
$ x: chr \a\ \a\ \a\ \a\ ...
$ y: num 1.94 1.18 1.24 2.62 1.11 ...
r
r # show scatter plot of categorical x
variable sim2 %>% ggplot() + geom_point(aes(x, y))

r
r # fit a simple model and generate predictions again mod2 <- lm(y ~ x, data = sim2) grid <- sim2 %>% data_grid(x) %>% add_predictions(mod2) grid
Modeling categorical variables
- Q: Why does a model with categorical
x
will just predict the mean value for each category?
- Q: What is our distance criterion for fitting the “best” model?
r
r sim2 %>% ggplot(aes(x)) + geom_point(aes(y = y)) + geom_point(data = grid, aes(y = pred), colour = , size = 4)

Modeling with interactions
- sometimes we need to combine information about a quantitative & a categorical variable in the same model
- Interpretation: we can’t explain the impact of one variable (x1) without considering another variable (x2)
- toy example
- data:
sim3
- note: I’m using slightly irregular syntax to clarify the point
r
r # inspect data structures str(sim3)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 120 obs. of 5 variables:
$ x1 : int 1 1 1 1 1 1 1 1 1 1 ...
$ x2 : Factor w/ 4 levels \a\,\b\,\c\,\d\: 1 1 1 2 2 2 3 3 3 4 ...
$ rep: int 1 2 3 1 2 3 1 2 3 1 ...
$ y : Named num -0.571 1.184 2.237 7.437 8.518 ...
..- attr(*, \names\)= chr \a\ \a\ \a\ \b\ ...
$ sd : num 2 2 2 2 2 2 2 2 2 2 ...
r
r # show scatter plot of categorical x
variable sim3 %>% ggplot() + geom_point(aes(x = x1, y = y, color = x2))

r
r # fit a simple model and generate predictions again mod3_1 <- lm(y ~ x1 + x2, data = sim3) mod3_2 <- lm(y ~ x1 + x2 + x1*x2, data = sim3) # generate model predictions grid <- sim3 %>% data_grid(x1, x2) %>% add_predictions(mod3_1, var = 1) %>% add_predictions(mod3_2, var = 2) grid
Plot the interaction model
- we should stack (i.e. gather) the predictions so we can make the model comparisons on the same plot
- Q: What’s happening here? recall…
mod3_1 <- lm(y ~ x1 + x2, data = sim3)
mod3_2 <- lm(y ~ x1 + x2 + x1*x2, data = sim3)
- interpretation of interaction
r
r gridStack <- grid %>% gather(key = , value = , mod1, mod2) # model plot sim3 %>% ggplot(aes(x = x1, y = y, color = x2)) + geom_point() + geom_line(data = gridStack, aes(y = pred)) + facet_wrap(~ model)

r
r # same model, but facets separate each model & x2 combination sim3 %>% ggplot(aes(x = x1, y = y, color = x2)) + geom_point() + geom_line(data = gridStack, aes(y = pred)) + facet_grid(model ~ x2)

Write down each model
- many people understand the interaction better by writing down the models
- here’s the model output so we can work them out
r
r summary(mod3_1)
Call:
lm(formula = y ~ x1 + x2, data = sim3)
Residuals:
Min 1Q Median 3Q Max
-4.4674 -0.8524 -0.0729 0.7886 4.3005
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.87167 0.38738 4.832 4.22e-06 ***
x1 -0.19674 0.04871 -4.039 9.72e-05 ***
x2b 2.88781 0.39571 7.298 4.07e-11 ***
x2c 4.80574 0.39571 12.145 < 2e-16 ***
x2d 2.35959 0.39571 5.963 2.79e-08 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1.533 on 115 degrees of freedom
Multiple R-squared: 0.5911, Adjusted R-squared: 0.5768
F-statistic: 41.55 on 4 and 115 DF, p-value: < 2.2e-16
r
r summary(mod3_2)
Call:
lm(formula = y ~ x1 + x2 + x1 * x2, data = sim3)
Residuals:
Min 1Q Median 3Q Max
-2.87634 -0.67655 0.04837 0.69963 2.58607
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.30124 0.40400 3.221 0.00167 **
x1 -0.09302 0.06511 -1.429 0.15587
x2b 7.06938 0.57134 12.373 < 2e-16 ***
x2c 4.43090 0.57134 7.755 4.41e-12 ***
x2d 0.83455 0.57134 1.461 0.14690
x1:x2b -0.76029 0.09208 -8.257 3.30e-13 ***
x1:x2c 0.06815 0.09208 0.740 0.46076
x1:x2d 0.27728 0.09208 3.011 0.00322 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1.024 on 112 degrees of freedom
Multiple R-squared: 0.8221, Adjusted R-squared: 0.811
F-statistic: 73.93 on 7 and 112 DF, p-value: < 2.2e-16
Plot the model (mosaic::plotModel()
)
- note: for simpler model forms, there are good functions to help (e.g.,
mosaic
package)
- same models as before:
mod3_1 <- lm(y ~ x1 + x2, data = sim3)
mod3_2 <- lm(y ~ x1 + x2 + x1*x2, data = sim3)
mplot()
is a pretty handy function
- console:
mplot(sim1)
opens interactive graph-builder to get started with EDA
- regression:
mplot(modelFit)
produces a few common residual plots
- choose a single plot with
mplot(modelFit, which = 1)
- four-pack of common plots with
mplot(modelFit)
- other options available
r
r # reproduce our model plots plotModel(mod3_1) # no interaction (parallel lines)

r
r plotModel(mod3_2) # interaction (non-parallel…slope can vary by group)

r
r # residuals vs fitted values mplot(mod3_1, which = 1)
[[1]]

r
r mplot(mod3_2, which = 1)
[[1]]

r
r # what’s this? mplot(mod3_2, which = 7)
[[1]]

Interactions (continuous:continuous)
- Recall: Continuous:Categorical interaction allows different slope & intercept depending on the value of categorical variable
- Similarly: Continuous:Continuous interaction allows different slope & intercept depending on the value of the other continuous variable.
- Think of a twisted piece of graph paper… every combination of X1:X2 is still linear, but the slopes and intercepts vary
- Sometimes plotted as 3D wire diagrams (like the graph paper idea)
- Often plotted as 2D heat map or contour plot
Oridinary Least Squares (OLS) Regression Summary
- Linear relationships are very convenient for interpret and understand
- Multiple regression is a great tool for attacking lots of interesting questions
- The assumptions are pretty straight-forward (L-I-N-E)
- Linear relationship between the mean response (Y) and each explanatory variable (X)
- observations are Independent
- responses are Normally distributed at each level of X
- variance of the response is Equal for all levels of X

OLS Regression Fail
Problem: lots of interesting questions can’t be answered by data that satisfy those conditions.
Consider a couple of examples:
- Grades & Studying
- Is the time spent studying predictive of success on an exam? The time spent studying for an exam (in hours) and result (pass/fail) are recorded for randomly selected students.
- since the objective is ultimately to associate the probability of success, \(p\), with the covariates, one might be tempted to try a linear model for the average binary response, \(p\): \(p_{i} = \beta_{0} + \beta_{1}x_{i}\)
- Q: What’s the OLS violation?
- e.g., the response variable is binary, which violates the OLS assumption of a normally distributed response at each level of X
- Result: \(p_{i} = \beta_{0} + \beta_{1}x_{i}\) doesn’t work well for bernoulli/binomial response

OLS Regression Fail
Problem: lots of interesting questions can’t be answered by data that satisfy those conditions.
Consider a couple of examples:
- ER Visits & Pollution:
- Is the number of asthma-related visits to a hospital Emergency Room associated with the air quality index for the day? The total number of ER patients (count) and the air quality index (continuous measurement)
- one might be tempted to try and model \(\lambda_{i}\) as a linear function of the explanatory variable: \(\lambda_{i} = \beta_{0} + \beta_{1}x_{i}\)
- Q: What’s the OLS violation?
- e.g., the response variable is Poisson so we expect that mean = variance = \(\lambda\), which violates the OLS assumption of equal variance for all levels of X
- Result: \(\lambda_{i} = \beta_{0} + \beta_{1}x_{i}\) doesn’t work well for Poisson response

OLS Regression Fail
Ecologists: |
Number of Species |
Poisson |
Criminologists: |
Arrest Count |
Poisson |
Cancer specialist: |
Number of cases |
Poisson |
Political Scientists: |
Who’s a democrate? |
Bernoulli or Binomial |
Pre-med Student: |
Who gets into med school? |
Bernoulli or Binomial |
Sociologist: |
Who’s got a tattoo? |
Bernoulli or Binomial |
Generalized Linear Models (GLMs)
One-parameter exponential family
- In the early 1970s Nelder & Wedderburn identified a broader class of models that generalizes the multiple linear regression approach under certain conditions:
- The probability formula (pdf or pmf) can be written as follows:
\[f(y; \theta)=exp^{(a(y)b(\theta) + c(\theta) + d(y))}\]
- the set of possible values (i.e. the support) does not depend upon a parameter
If so, it is said to have one-parameter exponential family form…
…Here’s the cool part
- \(\mu = -\frac{c'(\theta)}{b'(\theta)}\)
- \(\sigma^2 = \frac{b''(\theta)c'(\theta) - c''(\theta)b'(\theta)}{[b'(\theta)]^3}\)
- \(b(\theta) = \beta_0 + \beta_1X + ...\) is a linear model!
- \(b(\theta)\) is called the canonical link function, meaning it’s often a good choice to model as a linear function of the explanatory variables (but other link functions exist and might be preferred under special circumstances).
GLM gut-check (Poisson)
Goal:
\[f(y; \theta)=exp^{(a(y)b(\theta) + c(\theta) + d(y))}\]
What if our response variable is…
Poisson?
- support: since possible values for any Poisson random variable range from \(0\) to \(\infty\), the support doesn’t depend on \(\lambda\)
- \(P(Y=y) = \frac{e^{-\lambda}\lambda^y}{y!}\)
- since \(\lambda^y = e^{ylog(\lambda)}\)
- \(P(Y=y) = e^{-\lambda}e^{ylog(\lambda)}e^{-log(y!)}\)
- \(P(Y=y) = e^{ylog(\lambda) - \lambda - log(y!)}\)
- then, \(b(\theta) = log(\lambda)\)
GLM gut-check (Binomial)
Goal:
\[f(y; \theta)=exp^{(a(y)b(\theta) + c(\theta) + d(y))}\]
What if our response variable is…
Binomial?
- support: for any value of \(p\), \(0 < p < 1\), all integer values from 0 to \(n\) are possible so the support doesn’t depend on \(p\).
- \(P(Y=y) = {n\choose{y}}p^y(1-p)^{n-y}\)
- \(P(Y=y) = e^{ylog(p) + (n-y)log(1-p) + log{n\choose{y}}}\)
- \(P(Y=y) = e^{ylog(\frac{p}{1-p}) + nlog(1-p) + log{n\choose{y}}}\)
- then, \(b(\theta) = log(\frac{p}{1-p})\)
- this is called a logit function, and it is interpreted as the log of the odds of “success” where the observed outcome is deemed either “success” or “failure”
GLM gut-check (Normal)
Goal:
\[f(y; \theta)=exp^{(a(y)b(\theta) + c(\theta) + d(y))}\]
What if our response variable is…
…Normal?
- \(f(y) = \frac{1}{\sigma\sqrt(2\pi)}e^{-\frac{(y - \mu)^2}{2\sigma^2}}\)
- \(f(y) = e^{-log(\sigma)-log(2\pi)/2}e^{-\frac{(y - \mu)^2}{2\sigma^2}}\)
- not looking good for our hero, but don’t panic… what if we assume \(\sigma\) is known (i.e. constant)?
- \(f(y) \propto e^{-(-2y\mu + \mu^2 + y^2)}\)
- then, \(b(\theta) = \mu\)
- so, \(\mu_{Y|X} = \beta_{0} + \beta_{1}X\)
- and, of course, possible values for any Normal random variable range from \(-\infty\) to \(\infty\), so the support looks good!
- Does this square with what you already know about multiple regression with a Normal response?
Who’s in the family?
Here are some other distributions that can be written in one-parameter exponential family form:
- Bernoulli
- Binomial
- Poisson
- Normal
- Exponential
- Gamma
- Geometric
- Negative Binomial
- and some others that aren’t as common
Poisson Regression
- One-page summary of BYSH Chapter 4
- Poisson Regression Assumptions (L-M=V-I? …not as catchy):
- Linearity: the log of the mean rate \(log(\lambda_{i})\) must be a linear function of x
- Mean = Variance by definition because it’s Poisson
- Independence
- If the response throws you a curveball like overdispersion or too many zeroes there are sensible adjustments available
- Accounting for overdispersion (BYSH, p. 80)
- Zero-inflated Poisson (ZIP) model (BYSH, p. 84)
- in R:
glm(Y ~ X, family = poisson)
- note: glm function and not the old lm
- interpreting coefficient \(\beta_{1}\)
- since \(log(\lambda_{X}) = \beta_{0} + \beta_{1}X\)
- then, \(e^{\beta_{1}X} = \frac{\lambda_{X + 1}}{\lambda_{X}}\) (this is called a “rate ratio” or sometimes “relative risk”)
- generically, the mean response changes by a factor of \(e^{\beta_{1}}\) with each unit increase in X. For example, if \(e^{\beta_{1}} = 0.87\) you describe it as a 13% decrease, and if \(e^{\beta_{1}} = 5.3\) the mean response is 5.3 times (crudely, 530%) higher.
Logistic Regression
- One-page summary of BYSH Chapter 6
- Binomial Regression Assumptions:
- Binomial response is a sum of \(n\) Bernoulli trials
- Trials are independent
- \(n\) is fixed (note: \(n = 1\) is fine)
- \(p\) is the same for all trials
- Overdispersion adjustment (BYSH, p. 114)
- in R:
glm(Y ~ X, family = binomial)
- note: glm function and not the old lm
- interpreting coefficient \(\beta_{1}\)
- since \(log(\frac{p}{1-p}) = \beta_{0} + \beta_{1}X\)
- then, \(e^{\beta_{1}} = \frac{p_{1}/(1-p_{1})}{p_{0}/(1-p_{0})}\) (this is an odds ratio)
- also, \(p = \frac{e^{\beta_{0}} + e^{\beta_{1}X}}{1 + e^{\beta_{0}} + e^{\beta_{1}X}}\) (that’s the probability of “success” given X)
- generically, the odds of “success” change by a factor of \(e^{\beta_{1}}\) with each unit increase in X. For example, if \(e^{\beta_{1}} = 0.10\) the odds have decreased by 90%, and if \(e^{\beta_{1}} = 10.0\) the odds of “success” are 10 times (crudely, 1000%) higher.
One last time… Back to Chris

- What if we decide to approach the slight delay differently…
- Maybe what’s really important to Chris is whether or not the flight departs on time (within 10 minutes)
- If the plane leaves on-time and then arrival is delayed by some problem at the destination, perhaps the client would be understanding.
- Response:
timelyDepart
coded {TRUE; FALSE} is binary
- Explanatory Variables:
hour
of the day; dow
day of the week
r
r require(lubridate) # some minor wrangling (what are we doing here?) OrdColleagueData <- OrdColleagueData %>% mutate(timelyDepart = if_else(dep_delay < 10, TRUE, FALSE), date = ymd(paste0(year, -, month, -, day)), dow = as.character(wday(date, label = TRUE)))
Plot the relationship
- Let’s look at on-time departure status by hour of the day for our data
r
r OrdColleagueData %>% filter(!is.na(timelyDepart)) %>% ggplot(aes(x = hour, y = as.numeric(timelyDepart))) + geom_jitter(alpha = 0.3, height = 0.05) + geom_smooth(method = , method.args = list(family = ), se = 0) + ylab(-Time Departure Status)

Logistic Regression Modeling
- Again, we should write down the model to be sure we understand it…
- note about dispersion and “quasibinomial”
r
r logitMod <- glm(timelyDepart ~ hour + dow, family = , data = OrdColleagueData) msummary(logitMod)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 2.8451 0.7127 3.992 6.56e-05 ***
hour -0.1490 0.0411 -3.625 0.000289 ***
dowMon 0.5220 0.5593 0.933 0.350661
dowSat 0.6658 0.6786 0.981 0.326543
dowSun 0.2946 0.5819 0.506 0.612704
dowThu -0.2611 0.5409 -0.483 0.629289
dowTue 1.4252 0.7329 1.945 0.051833 .
dowWed 0.5224 0.5813 0.899 0.368810
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 241.83 on 217 degrees of freedom
Residual deviance: 218.63 on 210 degrees of freedom
(12 observations deleted due to missingness)
AIC: 234.63
Number of Fisher Scoring iterations: 4
r
r confint(logitMod)
Waiting for profiling to be done...
2.5 % 97.5 %
(Intercept) 1.51072125 4.32058967
hour -0.23324773 -0.07126707
dowMon -0.56796752 1.64527078
dowSat -0.61479789 2.09876835
dowSun -0.84017869 1.46041787
dowThu -1.33438665 0.80045987
dowTue 0.08024228 3.03437106
dowWed -0.60520070 1.69755918
Other model families
- Once you’ve mastered linear models, it’s easy to learn the mechanics of other model classes:
- Generalised linear models (GLM;
stats::glm()
): use a statistical idea called “likelihood” to accommodate either a continuous or non-continuous response variable (e.g. binary, binomial, or counts)
- Generalised additive models (GAM;
mgcv::gam()
): extend GLM to incorporate arbitrary smooth functions
- Penalized linear models (e.g., Lasso;
glmnet::glmnet()
): add a penalty term to the distance metric in roder to reduce model complexity in an attempt to produce models that generalize better to new (out-of-sample) datasets from the same population
- Robust linear models (
MASS::rlm()
): tweak the distance metric to be less sensitive to outliers, at the cost of being not quite as efficient when there are no outliers.
- Trees (e.g. CART;
rpart::rpart()
): uses recursive partitioning to fit a piece-wise constant model, splitting the data into progressively smaller and smaller pieces.
- Most effective when used in aggregate
- random forests (
randomForest::randomForest()
)
- gradient boosting machines (
xgboost::xgboost()
)
LS0tCnRpdGxlOiAiU3RhdGlzdGljYWwgTW9kZWxpbmcgRm91bmRhdGlvbnMiCnN1YnRpdGxlOiAiTURTUiBDaCA3ICYgUjREUyBDaCAyMi0yMyAmIE1EU1IgQXBwZW5kaXggRSIKb3V0cHV0OiAKICBzbGlkeV9wcmVzZW50YXRpb246IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0IAotLS0KCgpgYGB7ciBGcm9udCBNYXR0ZXIsIGV2YWw9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBnbG9iYWwgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbD1UUlVFLCBpbmNsdWRlPVRSVUUpCiMgb3B0aW9ucyhuYS5hY3Rpb24gPSBuYS53YXJuKQoKIyBjbGVhbiB1cCBSIGVudmlyb25tZW50CnJtKGxpc3QgPSBscygpKQoKIyBsb2FkIGFsbCBwYWNrYWdlcyBoZXJlCmxpYnJhcnkobWRzcikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobW9zYWljKQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykKbGlicmFyeShtb2RlbHIpCmxpYnJhcnkobHVicmlkYXRlKQoKCiMjIyB1c2VyLWRlZmluZWQgZnVuY3Rpb25zIGhlcmUgKGlmIGFueSkKIyBgbW9kZWwxYCBjYWxjdWxhdGVzIHByZWRpY3Rpb25zIGZvciBhIGdpdmVuIG1vZGVsCiMgYG1lYXN1cmVfZGlzdGFuY2VgIGNhbGN1bGF0ZXMgb3VyIGRpc3RhbmNlIG1ldHJpYyBmb3IgYSBnaXZlbiBtb2RlbAojIGBzaW0xX2Rpc3RgIGhlbHBlciBmdW5jdGlvbiBmb3IgYHB1cnJyOjptYXAyKClgIGV4ZWN1dGlvbgoKIyBpbnB1dHMgKGUuZy4gZGF0YSkgc3VtbWFyeQpkYXRhKCJmbGlnaHRzIikgICMgZnJvbSBgbnljZmxpZ2h0czEzYApkYXRhKCJzaW0xIikgICAgICMgZnJvbSBgbW9kZWxyYApkYXRhKCJzaW0yIikgICAgICMgZnJvbSBgbW9kZWxyYApkYXRhKCJzaW0zIikgICAgICMgZnJvbSBgbW9kZWxyYApkYXRhKCJzaW00IikgICAgICMgZnJvbSBgbW9kZWxyYAoKYGBgCgoKIyMgQWdlbmRhCgotIHJlc291cmNlcwogICAgLSBNRFNSIENoYXB0ZXIgNwogICAgLSBSIGZvciBEYXRhIFNjaWVuY2UgQ2hhcHRlcnMgMjItMjMgKDxodHRwczovL3I0ZHMuaGFkLmNvLm56L21vZGVsLWludHJvLmh0bWw+KQotIHN0YXRpc3RpY2FsIG1vZGVsaW5nIGZvdW5kYXRpb25zCiAgICAtIFNhbXBsZXMgJiBwb3B1bGF0aW9ucwogICAgLSBTYW1wbGUgc3RhdGlzdGljcwogICAgLSBCb290c3RyYXBwaW5nCiAgICAtIE91dGxpZXJzCiAgICAtIFJlZ3Jlc3Npb24KCgojIyMjIEFubm91bmNlbWVudHMKCi0gS2VlcCB1cCB3aXRoIFBpYXp6YQotIENhbnZhcyBhc3NpZ25tZW50cyAKICAgIC0gTURTUiBDaGFwIDUgRXhlcmNpc2VzCiAgICAtIE1pbmktUHJvamVjdDogQmlrZSBTaGFyZSBhbmQgTG9jYWwgV2VhdGhlcgogICAgLSBQcm9ncmFtbWluZyBOb3RlYm9va3M6IE1EU1IgQ2hhcHRlciAwNwogICAgICAgIC0gWW91ciByZXN1bHRzIG1heSBkaWZmZXIgZnJvbSB0aGUgYm9vayBpbiBTZWMgNy4yICYgU2VjIDcuMyAoaXQncyBPSykKICAgICAgICAtIGxpa2VseSBgc2V0LnNlZWQoKWAgaXNzdWUsIHNvIHJhbmRvbS9zaW11bGF0ZWQgcHJvY2Vzc2VzIGRpZmZlcgoKCiMjIFN0YXRpc3RpY2FsIEZvdW5kYXRpb25zCgotICoqR29hbDogZXh0cmFjdCAqbWVhbmluZyogZnJvbSBkYXRhKiogIAotIENvbW1vbiBwaXRmYWxsLi4uCiAgICAtIGRvbid0ICp1bmRlcmVzdGltYXRlKiBpbXBvcnRhbmNlIG9mIHZpc3VhbGl6YXRpb24gCiAgICAtIGRvbid0ICpvdmVyZXN0aW1hdGUqIGltcG9ydGFuY2Ugb2YgYW5hbHl0aWNhbCByZXN1bHRzIAogICAgLSBib3RoIGhhdmUgaW1wb3J0YW50IGFuZCBjb21wbGltZW50YXJ5IHJvbGVzIAoKIVtJbWFnZSBjcmVkaXQ6IFIgZm9yIERhdGEgU2NpZW5jZSAoaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9tb2RlbC1pbnRyby5odG1sKV0ocjRkc19zMjItMC5wbmcpCgoKIyMgS2V5IGlkZWFzOiBTYW1wbGVzLCBwb3B1bGF0aW9ucywgc2FtcGxpbmcgZGlzdHJpYnV0aW9ucwoKLSBvdXIgZGF0YSBhcmUganVzdCBhICoqc2FtcGxlKiogcmVwcmVzZW50YXRpdmUgb2Ygc29tZSBsYXJnZXIgcG9wdWxhdGlvbiAob3IgcHJvY2VzcykKICAgIC0gdGhlIHNhbXBsZSBpcyAqcmFuZG9tKgogICAgLSBpZiBhbm90aGVyIHBlcnNvbiB3ZXJlIHRvIHJlcGxpY2F0ZSB0aGUgc3R1ZHksIHRoZXkgd291bGQgZ2V0IGEgZGlmZmVyZW50IHNhbXBsZQogICAgLSBpZiB3ZSB3ZXJlIHRvIGRvIHRoZSBzdHVkeSBhZ2FpbiBpbiB0aGUgZnV0dXJlLCB3ZSB3b3VsZCBnZXQgYSBkaWZmZXJlbnQgc2FtcGxlCi0gd2UgY2FyZSBhYm91dCBwYXR0ZXJucy9zdHJ1Y3R1cmUvbW9kZWxzIG9mIHRoZSAqKnBvcHVsYXRpb24qKgogICAgLSBhIGdvb2QgbW9kZWwgaW5jbHVkZXMgc2FsaWVudCBmZWF0dXJlcyBvZiB0aGUgcG9wdWxhdGlvbiB0byByZXN1bHQgaW4gc2ltaWxhciBvdXRjb21lcyBhY3Jvc3MgKHJhbmRvbSkgc2FtcGxlcwotIHRoZSAqKnNhbXBsaW5nIGRpc3RyaWJ1dGlvbioqIGlzIHNpbXBseSBhIGRpc3RyaWJ1dGlvbiBvZiBwbGF1c2libGUgb3V0Y29tZXMgdGhhdCB3b3VsZCBiZSBvYnNlcnZlZCBpZiB0aGUgc3R1ZHkgd2VyZSByZXBlYXRlZCBpbmZpbml0ZWx5IG1hbnkgdGltZXMuCiAgICAtIGJhc2ljYWxseSwgdGhpcyBpcyBhIGh5cG90aGV0aWNhbCBkaXN0cmlidXRpb24gb2YgYSBzYW1wbGUgc3RhdGlzdGljIGFtb25nIGFsbCBwb3NzaWJsZSBzYW1wbGVzIG9mIGEgZ2l2ZW4gc2l6ZQogICAgLSB0aGlzIHByaW1hcmlseSBnaXZlcyB1cyBhIHNlbnNlIG9mIHRoZSAqdmFyaWFiaWxpdHkqIHRvIGJlIGV4cGVjdGVkIGZyb20gb25lIHNhbXBsZSB0byBhbm90aGVyCgoKIyMgU3RhdGlzdGljYWwgRm91bmRhdGlvbnMKCi0gd2Ugb2Z0ZW4gZG9uJ3QgYWN0dWFsbHkgY2FyZSB0aGF0IG11Y2ggYWJvdXQgdGhlIHNwZWNpZmljIHNhbXBsZSB0aGF0IHdlIGhhdmUKICAgIC0gYSBzYW1wbGUgaXMgYSBvbmUtdGltZSByZWFsaXphdGlvbiBvZiBzb21lIGRhdGEgY29sbGVjdGlvbiBwcm9jZXNzCiAgICAtIGlmIHdlIHJlcGVhdCB0aGUgc3R1ZHkvaW52ZXN0aWdhdGlvbiBhbmQgY29sbGVjdCBuZXcgZGF0YSB3ZSBhbG1vc3QgY2VydGFpbmx5IG9ic2VydmUgYSBkaWZmZXJlbnQgc2FtcGxlCiAgICAtIGlmIHdlIGNhbid0IG1ha2UgY29uY2x1c2lvbnMgKmJleW9uZCogdGhlIHNwZWNpZmljIHNhbXBsZSB3ZSBoYXBwZW5lZCB0byBlbmNvdW50ZXIsIHdlIHR5cGljYWxseSBoYXZlbid0IGdhaW5lZCBtdWNoCi0gd2UgdHlwaWNhbGx5IGNhcmUgZmFyIG1vcmUgYWJvdXQgd2hhdCB0aGUgc2FtcGxlICoqcmVwcmVzZW50cyoqIChhbmQgaXQncyBsaW1pdGF0aW9ucykKICAgIC0gZXhwbG9yYXRpb24gb3IgZXhwbGFuYXRpb24gb2Ygc29tZSBsYXJnZXIgcG9wdWxhdGlvbiBvciBwcm9jZXNzCiAgICAtIG91dGNvbWVzL3JlbGF0aW9uc2hpcHMgaW4gdGhlIGRhdGEgdGhhdCBjYW4gYmUgcmVwbGljYXRlZC9yZXByb2R1Y2VkCiAgICAtIGZ1dHVyZSBwcmVkaWN0aW9ucwogICAgLSBhbmQgY2FyZWZ1bGx5IHF1YW50aWZ5aW5nIHVuY2VydGFpbnR5IG9mIHRoZSBvdXRjb21lcyBhYm92ZQoKCiMjIEV4YW1wbGU6IG1lZXQgQ2hyaXMKCiFbXShjaHJpcy5wbmcpCgotIE1vdGl2YXRpb246IAogICAgLSBBIGNsb3NlIGZyaWVuZCBvZiBtaW5lIGlzIGFuIGV4ZWN1dGl2ZSBtYW5hZ2VtZW50IGNvbnN1bHRhbnQgd2l0aCBQd0MKICAgIC0gQ2hyaXMgbGl2ZXMgb24gdGhlIEVhc3QgQ29hc3QsIGJ1dCB0cmF2ZWxzIChNLVRoKSBldmVyeSB3ZWVrIGZvciB3b3JrCiAgICAtIEhpcyBwcmltYXJ5IGNsaWVudHMgKHJpZ2h0IG5vdykgYXJlIG9uIFdlc3QgQ29hc3QgJiBNaWR3ZXN0LgogICAgLSBGbGlnaHQgZGVsYXlzIGFyZSBhIG1ham9yIHRocmVhdCwgYmVjYXVzZSBpZiB0aGUgY2xpZW50IGNhbid0IHJlbHkgb24gaGltIHRvIGJlIHRoZXJlIG9uIHRpbWUgdGhleSBtYXkgZ2l2ZSB0aGUgcHJvamVjdCB0byBhIGRpZmZlcmVudCBjb25zdWx0YW50LgotICoqR29hbDogSGUgd2FudHMgdG8gbWF4aW1pemUgdGltZSBhdCBob21lIHdpdGggaGlzIHdpZmUgYW5kIGtpZHMsIHdpdGhvdXQgamVvcGFyZGl6aW5nIHdvcmsgY29tbWl0bWVudHMqKgogICAgLSBIb3cgZWFybHkgc2hvdWxkIENocmlzIHNob3VsZCBwbGFuIHRvIGFycml2ZSBpbiBDaGljYWdvIChPUkQpIGlmIGhlIHdhbnRzIHRvIGF2b2lkIG1pc3NpbmcgbWVldGluZ3MgZHVlIHRvIGZsaWdodCBkZWxheXM/CiAgICAtIFE6IElkZWFzIGhvdyB0byBhcHByb2FjaCB0aGUgcHJvYmxlbT8KICAgIAoKYGBge3IgZXZhbD1UUlVFfQpyZXF1aXJlKHRpZHl2ZXJzZSkKcmVxdWlyZShtZHNyKQpyZXF1aXJlKG1vc2FpYykKcmVxdWlyZShueWNmbGlnaHRzMTMpCgpkYXRhKGZsaWdodHMpCgojIEdldCBmbGlnaHRzIGZyb20gTllDIHRvIE9SRApPcmREZWxheXMgPC0gCiAgZmxpZ2h0cyAlPiUKICBmaWx0ZXIoZGVzdCA9PSAiT1JEIikKYGBgCgoKCiMjIEV4YW1wbGU6IGZsaWdodCBkZWxheXMgZnJvbSBOZXcgWW9yayB0byBDaGljYWdvCgpgYGB7ciBldmFsPVRSVUV9Ck9yZERlbGF5czI4IDwtIAogIE9yZERlbGF5cyAlPiUKICBzYW1wbGVfbihzaXplID0gMjgpCmBgYAoKLSBDaHJpcyBmbGllcyBhIGxvdCBhbmQga25vd3Mgc29tZSBzdGF0aXN0aWNzLCBidXQgbGV0cyBzdXBwb3NlIGhlIG9ubHkgaGFzIGRhdGEgZnJvbSAyOCBmbGlnaHRzCiAgICAtIEhlcmUgYXJlIHNvbWUgc3VtbWFyeSBzdGF0aXN0aWNzCiAgICAtIFE6IEhvdyBzaG91bGQgaGUgZGVjaWRlIHdoZW4gdG8gYXJyaXZlPwoKYGBge3IgZXZhbD1UUlVFfQojIHN1bW1hcnkgc3RhdGlzdGljcyBvZiBkZWxheXMKZmF2c3RhdHMoIH4gYXJyX2RlbGF5LCBkYXRhID0gT3JkRGVsYXlzMjgpCmBgYAoKIyMgRXhhbXBsZTogZmxpZ2h0IGRlbGF5cyBmcm9tIE5ldyBZb3JrIHRvIENoaWNhZ28KCi0gVHJhZGUtb2ZmLi4uIE1heWJlIENocmlzIGNhbiByaXNrIGJlaW5nIGxhdGUgb25jZSBpbiBhIHdoaWxlIHRvIGhhdmUgbW9yZSB0aW1lIHdpdGggaGlzIGZhbWlseQotIEhlcmUncyB3aGF0IGhlIG1pZ2h0IGRlY2lkZSAqYmFzZWQgb24gaGlzIHNhbXBsZSoKICAgIC0gaGUgZmxpZXMganVzdCBhYm91dCBldmVyeSB3ZWVrCiAgICAtIDk3JSBwZXJjZW50IG9uLXRpbWUgd291bGQgbWVhbiB0aGF0IGhlIGV4cGVjdHMgdG8gYmUgbGF0ZSBhIGxpdHRsZSBtb3JlIHRoYW4gb25jZSBwZXIgeWVhci4KCmBgYHtyIGV2YWw9VFJVRX0KdG9sZXJhbmNlMjggPC0gcWRhdGEofiBhcnJfZGVsYXksIHAgPSAwLjk3LCBkYXRhID0gT3JkRGVsYXlzMjgpCnRvbGVyYW5jZTI4CgpgYGAKCiMjIEV4YW1wbGU6IGZsaWdodCBkZWxheXMgZnJvbSBOZXcgWW9yayB0byBDaGljYWdvCgotIENocmlzIGRvZXNuJ3QgaGF2ZSB0aGUgKipwb3B1bGF0aW9uKioKICAgIC0gaGUgY2FuJ3Qga25vdyBpZiBoaXMgaXMgYW4gZWZmZWN0aXZlIHBvbGljeSBvciBub3QuLi4gCiAgICAtIGRvZXMgdGhpcyAqKnJlYWxseSoqIHdvcmsgOTclIG9mIHRoZSB0aW1lPz8gIAogICAgLSBJcyBoZSB0b28gbGF0ZT8gIFRvbyBlYXJseT8/Ci0gSW4gb3VyIGh5cG90aGV0aWNhbCBleGVyY2lzZSwgd2UgKipkbyoqIGhhdmUgYSBwb3B1bGF0aW9uLCAKICAgIC0gd2UgY2FuIHNlZSBob3cgaGUgZGlkIAoKYGBge3IgZXZhbD1UUlVFfQojIHJlYWxpdHkgb2YgcG9wdWxhdGlvbiAodW5rbm93bi91bmtub3dhYmxlIHRvIENocmlzKQpmYXZzdGF0cyggfiBhcnJfZGVsYXksIGRhdGEgPSBPcmREZWxheXMpICAjIGZ1bGwgZGF0YQoKIyBwb3B1bGF0aW9uIHZhbHVlIGZvciAwLjk3IHF1YW50aWxlCnRvbGVyYW5jZSA8LSBxZGF0YSh+IGFycl9kZWxheSwgcCA9IDAuOTcsIGRhdGEgPSBPcmREZWxheXMpICAjIGZ1bGwgZGF0YQp0b2xlcmFuY2UKCiMgQWN0dWFsIHBlcmZvcm1hbmNlIGZvciBDaHJpcy4uLgp0YWxseSh+IGFycl9kZWxheSA8IHRvbGVyYW5jZTI4WzJdLCBkYXRhID0gT3JkRGVsYXlzLCBmb3JtYXQgPSAicHJvcG9ydGlvbiIpCmBgYAoKCgojIyBQcm9ibGVtIHNvbHZlZD8gIChOb3QgcXVpdGUpCgotIFRoZXJlJ3Mgc3RpbGwgcXVpdGUgYSBiaXQgb2YgcmlzayBkdWUgdG8gcmFuZG9tbmVzcy4uLiAKICAgIC0gYW4gaW50ZXJ2YWwgZXN0aW1hdGUgZm9yIHRoZSAwLjk3IHF1YW50aWxlIHdvdWxkIGJlIGJldHRlcgogICAgLSBROiAqKmhvdyBzaG91bGQgd2UgZG8gaXQ/KioKCmBgYHtyIGV2YWw9VFJVRX0KIyBSZXN1bHRzIHZhcnkuLi4gZm91ciBkaWZmZXJlbnQgc2FtcGxlcyA9IGZvdXIgZGlmZmVyZW50IHJlc3VsdHMKbiA8LSAyOAoKcWRhdGEofiBhcnJfZGVsYXksIHAgPSAwLjk3LCBkYXRhID0gc2FtcGxlX24oT3JkRGVsYXlzLCBzaXplID0gbiwgcmVwbGFjZSA9IFRSVUUpKQpxZGF0YSh+IGFycl9kZWxheSwgcCA9IDAuOTcsIGRhdGEgPSBzYW1wbGVfbihPcmREZWxheXMsIHNpemUgPSBuLCByZXBsYWNlID0gVFJVRSkpCnFkYXRhKH4gYXJyX2RlbGF5LCBwID0gMC45NywgZGF0YSA9IHNhbXBsZV9uKE9yZERlbGF5cywgc2l6ZSA9IG4sIHJlcGxhY2UgPSBUUlVFKSkKcWRhdGEofiBhcnJfZGVsYXksIHAgPSAwLjk3LCBkYXRhID0gc2FtcGxlX24oT3JkRGVsYXlzLCBzaXplID0gbiwgcmVwbGFjZSA9IFRSVUUpKQpgYGAKCiMjIERpc3RyaWJ1dGlvbiBvZiB0aGUgZXN0aW1hdGUgZGVwZW5kcyBvbiBzYW1wbGUgc2l6ZQoKLSBMZXQncyBzaW11bGF0ZSAxMDAwIHNhbXBsZXMgZnJvbSBvdXIgYE9yZERlbGF5YCAoZnVsbCkgZGF0YQotIEFueSBvbmUgb2YgdGhlc2UgY291bGQgYmUgdGhlIHNhbXBsZSB0aGF0IENocmlzIGhhcHBlbmVkIHRvIG9ic2VydmUKLSBXZSBqdXN0IHdhbnQgdG8gc2VlIGhvdyBtdWNoIHRoZXNlIHNhbXBsZXMgdmFyeSBmcm9tIG9uZSB0byB0aGUgbmV4dAoKYGBge3IgZXZhbD1UUlVFfQpuIDwtIDI4CgpTaW1zU21hbGxOIDwtIAogIG1vc2FpYzo6ZG8oMTAwMCkgKiBxZGF0YSh+IGFycl9kZWxheSwgcCA9IDAuOTcsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBzYW1wbGVfbihPcmREZWxheXMsIHNpemUgPSBuLCByZXBsYWNlID0gVFJVRSkpCgojIGluc3BlY3QgcmVzdWx0CmhlYWQoU2ltc1NtYWxsTikKCiMgdmFyaWFiaWxpdHkgb2Ygb3VyIDAuOTcgcXVhbnRpbGUgZXN0aW1hdGUgKG5vdGUgInNkIikKZmF2c3RhdHMofiBxdWFudGlsZSwgZGF0YSA9IFNpbXNTbWFsbE4pCmBgYAoKIyMjIyBWb2NhYnVsYXJ5CgotICpTYW1wbGUgc2l6ZSAobikqIGlzIHRoZSBudW1iZXIgb2YgY2FzZXMgaW4gYW4gb2JzZXJ2ZWQgc2FtcGxlCi0gKlNhbXBsaW5nIGRpc3RyaWJ1dGlvbiogaXMgdGhlIGNvbGxlY3Rpb24gb2YgdGhlIHNhbXBsZSBzdGF0aXN0aWMgZnJvbSBhbGwgdHJpYWxzCiAgICAtIGEgdGhlb3JldGljYWwgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIGluY2x1ZGVzIGFsbCBwb3NzaWJsZSB0cmlhbHMKICAgIC0gd2UgaGF2ZSAxMDAwIHNpbXVsYXRlZCB0cmlhbHMsIAogICAgICAgIC0gdGhhdCBzcGVjaWZpYyBudW1iZXIgZG9lc24ndCBtYXR0ZXIgbXVjaCBhcyBsb25nIGFzIGl0J3MgImxhcmdlIgogICAgICAgIC0gMTBzIG9mIHRob3VzYW5kcyBvciBtaWxsaW9ucyBhcmUgY29tbW9uIGluIHByYWN0aWNlCiAgICAgICAgLSBkbyBtb3JlIGlmIGEgcHJlY2lzZSByZXN1bHQgaXMgaW1wb3J0YW50Ci0gKnNoYXBlKiBvZiB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIGlzIHdvcnRoIG5vdGluZyAob3VycyBpcyByaWdodC1za2V3ZWQgaGVyZSkKLSAqc3RhbmRhcmQgZXJyb3IqIGlzIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBmb3IgYSBzdGF0aXN0aWMKICAgIC0gSGVyZSwgd2UgZXN0aW1hdGUgdGhlIFNFIHVzaW5nIHRoZSBTRCBvZiBtYW55IHNpbXVsYXRlZCAwLjk3IHF1YW50aWxlIGVzdGltYXRlcwogICAgLSB5b3UgY2FuIGZpbmQgdGhpcyByZXN1bHQgaW4gb3VyIGBmYXZzdGF0cygpYCBvdXRwdXQgYWJvdmUKCmBgYHtyIGV2YWw9VFJVRX0KU2ltc1NtYWxsTiAlPiUKICBnZ3Bsb3QoKSArIAogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gcXVhbnRpbGUpKSArIAogIGdndGl0bGUoIjEwMDAgc2ltdWxhdGVkIDAuOTcgcXVhbnRpbGVzIGZvciBmbGlnaHQgZGVsYXlzIGFtb25nIHNhbXBsZXMgb2YgbiA9IDI4IikKYGBgCgoKIyMgV2hhdCBpZiB3ZSBoYXZlIGEgYmlnZ2VyIHNhbXBsZT8KCi0gTWF5YmUgQ2hyaXMgY2FuIHBvb2wgZmxpZ2h0IGluZm9ybWF0aW9uIHdpdGggYSBmZXcgY29sbGVhZ3VlcyB0aGF0IHJlZ3VsYXJseSBmbHkgTllDIHJvIE9SRAogICAgLSBObyBmbGlnaHQgY291bnRlZCBtb3JlIHRoYW4gb25jZSAoaS5lLiBpZiB0aGV5IGhhZCBzaGFyZWQgYSBmbGlnaHQpCiAgICAtIFE6IFdvdWxkIGEgYmlnZ2VyIGRhdGEgc2V0IG1hdHRlcj8gIFdoYXQgZG8geW91IGV4cGVjdCB0byBjaGFuZ2U/CgpgYGB7ciBldmFsPVRSVUV9Cm5CaWdnZXIgPC0gMTUwCgpTaW1zQmlnTiA8LSAKICBtb3NhaWM6OmRvKDEwMDApICogcWRhdGEofiBhcnJfZGVsYXksIHAgPSAwLjk3LCAKICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gc2FtcGxlX24oT3JkRGVsYXlzLCBzaXplID0gbkJpZ2dlciwgcmVwbGFjZSA9IFRSVUUpKQoKIyBpbnNwZWN0IHJlc3VsdApoZWFkKFNpbXNTbWFsbE4pCgpgYGAKCiMjIyMgUGxvdCBjb21wYXJpc29uIChuID0gMjg7IG4gPSAxNTApCgpgYGB7ciBldmFsPVRSVUV9CiMgY29tYmluZSByZXN1bHRzIGludG8gYSBzaW5nbGUgdGJsCkJvdGhTaW1zIDwtIAogIGJpbmRfcm93cyhTaW1zU21hbGxOICU+JSBtdXRhdGUoU2FtcGxlU2l6ZSA9IG4pLCBTaW1zQmlnTiAlPiUgbXV0YXRlKFNhbXBsZVNpemUgPSBuQmlnZ2VyKSkgCgojIHN1bW1hcnkgY29tcGFyaXNvbgpmYXZzdGF0cyhxdWFudGlsZSB+IFNhbXBsZVNpemUsIGRhdGEgPSBCb3RoU2ltcykKCiMgcGxvdCBjb21wYXJpc29uCkJvdGhTaW1zICU+JQogIGdncGxvdChhZXMoeCA9IHF1YW50aWxlKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzApICsgCiAgZmFjZXRfZ3JpZCggfiBTYW1wbGVTaXplKSArCiAgeGxhYigiU2FtcGxlIDAuOTggUXVhbnRpbGUiKQpgYGAKCgoKIyMgQWNjZXNzIHRvIFNhbXBsaW5nIERpc3RyaWJ1dGlvbgoKLSBJbXBvcnRhbnQ6IGluIHRoZSBwcmV2aW91cyBleGVyY2lzZSB3ZSB3ZXJlIGFibGUgdG8gc2ltdWxhdGUgYW4gYWN0dWFsIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBiZWNhdXNlIHdlIHdlcmUgYWN0dWFsbHkgZHJhd2luZyByYW5kb20gc2FtcGxlcyBmcm9tIHRoZSBwb3B1bGF0aW9uLiAgCiAgICAtIFdlIG5lYXJseSBhbHdheXMganVzdCBoYXZlIG9uZSBzYW1wbGUKICAgIC0gV2UgbmVlZCBhbm90aGVyIHdheSB0byBhY2Nlc3MgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbgotIFdpdGgganVzdCBvbmUgc2FtcGxlIG9mIHRoZSBkYXRhIHdlIGhhdmUgYSBmZXcgb3B0aW9ucyB0byBjaGFyYWN0ZXJpemUgYSBzYW1wbGluZyBkaXN0cmlidXRpb24KICAgIC0gQW5hbHl0aWNhbCBzb2x1dGlvbnMgZGVyaXZlZCBmcm9tIHN0YXRpc3RpY2FsIHRoZW9yeSAoZS5nLiB0LXRlc3QpCiAgICAtIFNpbXVsYXRpb24tYmFzZWQgbWV0aG9kcwoKCiMjIEJvb3RzdHJhcHBpbmcKCi0gV2UgYXNzdW1lIG91ciBzYW1wbGUgaXMgcmVwcmVzZW50YXRpdmUgb2YgdGhlIHBvcHVsYXRpb24KLSBUaGUgcG9wdWxhdGlvbiBjYW4gYmUgcmVhc29uYWJseSBhcHByb3hpbWF0ZWQgYnkgbWFueSwgbWFueSwgbWFueSBjb3BpZXMgb2Ygb3VyIHNhbXBsZQotICoqSnVzdCBhcyB3ZSBoYWQgcHJldmlvdXNseSBkcmF3biBtYW55IHNhbXBsZXMgZnJvbSB0aGUgcG9wdWxhdGlvbiwgImJvb3RzdHJhcHBpbmciIGFwcHJveGltYXRlcyB0aGlzIHByb2Nlc3MgYnkgZHJhd2luZyBzaW11bGF0ZWQgc2FtcGxlcyAod2l0aCByZXBsYWNlbWVudCkgZnJvbSBvdXIgb3JpZ2luYWwgc2FtcGxlKioKCgojIyMjIERpc2N1c3Npb24gUXVlc3Rpb25zOiAKLSBXaHkgZG8gd2UgbmVlZCB0byBzYW1wbGUgKip3aXRoKiogcmVwbGFjZW1lbnQ/Ci0gRG9lcyBib290c3RyYXBwaW5nIGNvbGxlY3QgKipuZXcqKiBkYXRhPwotIERvIHlvdSBleHBlY3QgdGhlICoqbWVhbioqIG9mIHRoZSBib290c3RyYXAgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIHRvIGJlIGVxdWl2YWxlbnQgdG8gdGhlIG1lYW4gb2YgdGhlICh0cnVlKSBzYW1wbGluZyBkaXN0cmlidXRpb24/Ci0gRG8geW91IGV4cGVjdCB0aGUgKipzdGFuZGFyZCBlcnJvcioqIG9mIHRoZSBib290c3RyYXAgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIHRvIGJlIGVxdWl2YWxlbnQgdG8gdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSAodHJ1ZSkgc2FtcGxpbmcgZGlzdHJpYnV0aW9uPwoKCjwhLS0gRGF5MiAtLT4KCiMjIEJhY2sgdG8gQ2hyaXMuLi4KCioqQ2hyaXMgd2FudHMgdG8gYmUgbW9yZSBjb25maWRlbnQgdGhhdCBmbGlnaHQgZGVsYXlzIHdpbGwgbm90IG1ha2UgaGltIGxhdGUgbW9yZSB0aGFuIDMlIG9mIHRoZSB0aW1lLCB3aGF0IHNob3VsZCBoZSBkbz8qKiAgCi0gSGUgcG9vbHMgZmxpZ2h0IGRlbGF5IGRhdGEgd2l0aCBjb2xsZWFndWVzIGFuZCB0aGV5IGNvbWUgdXAgd2l0aCBhIG11Y2ggYmlnZ2VyIGRhdGEgc2V0IG9mIDIzMCBmbGlnaHRzCi0gV2UnbGwgY2FsbCBpdCBgT3JkQ29sbGVhZ3VlRGF0YWAKCiMjIEJvb3RzdHJhcCBjb25maWRlbmNlIGludGVydmFsCgoxLiB1c2luZyB0aGUgZGF0YSBjb21iaW5lZCBhbW9uZyBDaHJpcycgY29sbGVhZ3Vlcy0tYE9yZENvbGxlYWd1ZURhdGFgLS13ZSBzYW1wbGUgd2l0aCByZXBsYWNlbWVudCBhbmQgY2FsY3VsYXRlIHRoZSAwLjk3IHF1YW50aWxlIGVhY2ggdGltZS4KMi4gd2Ugbm93IGhhdmUgYSBkaXN0cmlidXRpb24gb2YgYm9vdHN0cmFwIHNhbXBsZSBxdWFudGlsZSBlc3RpbWF0ZXMgKDEwMDAgb2YgdGhlbSBpbiB0aGlzIGNhc2UpCjMuIHdlIHVzZSB0aGF0IGRpc3RyaWJ1dGlvbiB0byBwcm9kdWNlIGFuIGludGVydmFsIGVzdGltYXRlIAogICAgLSBJbiB0aGlzIGNhc2UsIGEgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgOTd0aCBwZXJjZW50aWxlIG9mIGFycml2YWwgZGVsYXkKCgojIyMjIGJvb3RzdHJhcCBzYW1wbGluZyBkaXN0cmlidXRpb24KYGBge3J9CiMgQ2hyaXMgYW5kIGNvbGxlYWd1ZXMgcG9vbCBkYXRhIGZvciAyMzAgc2FtcGxlcwpPcmRDb2xsZWFndWVEYXRhIDwtIAogIE9yZERlbGF5cyAlPiUKICBzYW1wbGVfbihzaXplID0gMjMwLCByZXBsYWNlID0gRkFMU0UpCgojIGJvb3RzdHJhcCBkaXN0cmlidXRpb24KQm9vdFN0cmFwVHJpYWxzIDwtIAogIG1vc2FpYzo6ZG8oMTAwMCkgKiBxZGF0YSh+IGFycl9kZWxheSwgcCA9IDAuOTcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gc2FtcGxlX24oT3JkQ29sbGVhZ3VlRGF0YSwgc2l6ZSA9IDIzMCwgcmVwbGFjZSA9IFRSVUUpKQoKYGBgCgojIyMjIGNvbmZpZGVuY2UgaW50ZXJ2YWwKCi0gSWYgdGhlIGJvb3RzdHJhcCBzYW1wbGluZyBkaXN0cmlidXRpb24gbG9va3MgYXBwcm94aW1hdGVseSBOb3JtYWw6IAogICAgLSBjb21wdXRlIHRoZSBDaHJpcydzIHNhbXBsZSBtZWFuCiAgICAtIGVzdGltYXRlIHRoZSBzdGFuZGFyZCBlcnJvciB1c2luZyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBib290c3RyYXAgc2FtcGxpbmcgZGlzdHJpYnV0aW9uCiAgICAtIENvbmZpZGVuY2UgaW50ZXJ2YWw6ICRlc3QgXHBtIHpeKiAoU0UpJAogICAgICAgIC0gImVzdCIgaXMgdGhlIGVzdGltYXRlIGZyb20gdGhlIHNhbXBsZSAoQ2hyaXMnIGRhdGEpCiAgICAgICAgLSAkel4qJCBpcyBhIGNyaXRpY2FsIHZhbHVlIGZyb20gdGhlIE5vcm1hbCBkaXN0cmlidXRpb24gY29tbWVuc3VyYXRlIHdpdGggY29uZmlkZW5jZSBsZXZlbCAoZS5nLiAxLjk2IGZvciA5NSUgQ0kpCiAgICAgICAgLSAkU0UkIGlzIHRoZSBzdGFuZGFyZCBlcnJvciB3aGljaCB3ZSd2ZSBlc3RpbWF0ZWQgZnJvbSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBib290c3RyYXAgZGlzdHJpYnV0aW9uCi0gSWYgYm9vdHN0cmFwIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBpcyBmYWlybHkgc21vb3RoLCBidXQgbm90IHN5bW1ldHJpYyAoZS5nLiBza2V3ZWQpLCAKICAgIC0gd2UgY291bGQgdXNlIGEgcGVyY2VudGlsZSBtZXRob2QgdG8gYXBwcm94aW1hdGUgb3VyIGNvbmZpZGVuY2UgaW50ZXJ2YWwKICAgIC0gbm90IHBlcmZlY3QsIGJ1dCBzdGlsbCB1c2VmdWwKLSBJZiBib290c3RyYXAgc2FtcGxpbmcgZGlzdGlyYnV0aW9uIGhhcyBiaWcgZ2FwcyBvciBvdGhlciBwcm9ibGVtcywgCiAgICAtIHdlIHNob3VsZCB0aGluayBoYXJkIGFib3V0IGFsdGVybmF0aXZlIHNvbHV0aW9ucy4uLiAKICAgIC0gZS5nLiwgd2hhdCdzIG1ha2luZyB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIHNvIHVnbHk/CgoKIyMjIyBPdXIgcmVzdWx0cz8KCi0gdGhlIGRpc3RyaWJ1dGlvbiBpcyBub3QgcHJldHR5Li4uIGl0J3Mgc29ydCBvZiBhIGJvcmRlcmxpbmUgY2FzZQogICAgLSBkZWZpbml0ZWx5IG5vdCBOb3JtYWwsIGJ1dCBwb3NzaWJseSBzdGlsbCB1c2VmdWwKICAgIC0gd2Ugc2hvdWxkIGV2ZW4gYmUgc3VzcGljaW91cyBvZiBhIGJvb3RzcmFwIHBlcmNlbnRpbGUgaW50ZXJ2YWwKICAgICAgICAtIHdlJ2xsIHN1cHBvc2UgaGUgd2FudHMgdG8gODAlIGNvbmZpZGVudCBoaXMgZmxpZ2h0cyB3aWxsIGFycml2ZSBvbiB0aW1lIDk3JSBvZiB0aGUgdGltZQotIERpc2N1c3Npb24gcXVlc3Rpb25zCiAgICAtIFE6IEhvdyBzaG91bGQgQ2hyaXMgZGVjaWRlIGhvdyBlYXJseSBoZSBzaG91bGQgcGxhbiB0byBhcnJpdmU/CiAgICAtIFE6IFdoeSBjb3VsZCBtYWtlIHRoaXMgYm9vdHN0cmFwIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBsb29rIHNvIHN0cmFuZ2U/CgpgYGB7cn0KQm9vdFN0cmFwVHJpYWxzICU+JQogIGdncGxvdCgpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBxdWFudGlsZSkpCgojIE1heWJlIENocmlzIGp1c3Qgd2FudHMgdG8gYmUgODAlIGNvbmZpZGVudCBoZSB3aWxsIGJlIG9uIHRpbWUgOTclIG9mIHRoZSB0aW1lCnFkYXRhKH4gcXVhbnRpbGUsIHAgPSAwLjgsIGRhdGEgPSBCb290U3RyYXBUcmlhbHMpCgpgYGAKCgoKIyMgT3V0bGllcnMKCi0gUGFydCBvZiB0aGUgcHJvYmxlbSBmb3IgQ2hyaXMgd2FzIHRoZSBmYWN0IHRoYXQgCiAgICAxLiB3ZSB3ZXJlIGludGVyZXN0ZWQgaW4gY2hhcmFjdGVyaXppbmcgdGhlIHRhaWwgb2YgdGhlIGRpc3JpYnV0aW9uOyAKICAgIDIuIHdlIGhhZCBhIGxvdCBvZiBleHRyZW1lIG9ic2VydmF0aW9ucyAocG90ZW50aWFsIG91dGxpZXJzKQotIEFuICoqb3V0bGllcioqIGlzIGFuIG9ic2VydmF0aW9uIHRoYXQgZG9lc24ndCBzZWVtIHRvIGNvbmZvcm0gdG8gdGhlIHBhdHRlcm4gb2YgdGhlIGRhdGEKICAgIC0gKipjb21wbGV0ZWx5IGxlZ2l0aW1hdGUgb2JzZXJ2YXRpb25zIG1heSBhcHBlYXIgdG8gYmUgb3V0bGllcnMqKgogICAgLSBwcm9ibGVtYXRpYyBvdXRsaWVycyBhcmUgY2FzZXMgdGhhdCBhcmUgZnVuZGFtZW50YWxseSBkaWZmZXJlbnQgZnJvbSB0aGUgcG9wdWxhdGlvbgogICAgICAgIC0gUTogZXhhbXBsZXM/Ci0gV2UgY2FuIGxlYXJuIGEgbG90IGJ5IHN0dWR5aW5nIGV4dHJlbWUgb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhCgojIyBMb25nIGRlbGF5cwoKLSBUaGUgbG9uZ2VzdCBkZWxheXMgKD4gNiBocnMpIHdlcmUgbW9zdCBvZnRlbiBhc3NvY2lhdGVkIHdpdGggQW1lcmljYW4gRWFnbGUgKE1RKSBhbmQgVW5pdGVkIEFpcmxpbmVzIChVQSkgYW5kIGZyZXF1ZW50bHkgb2NjdXIgaW4gdGhlIFNwcmluZyBtb250aHMgKE1hcmNoLCBBcHJpbCwgTWF5KS4gIAogICAgLSBROiBXaGF0IHNob3VsZCB3ZSBkbyB3aXRoIHRoaXMgaW5mb3JtYXRpb24/CgpgYGB7cn0KT3JkRGVsYXlzICU+JQogIGZpbHRlcihhcnJfZGVsYXkgPj0gMzYwKSAlPiUKICB0cmFuc211dGUoY2FycmllciwgbW9udGgsIGRheSwgZGVwX2RlbGF5LCBhcnJfZGVsYXksIGFpcl9kZWxheSA9IGFycl9kZWxheSAtIGRlcF9kZWxheSkgJT4lCiAgYXJyYW5nZShkZXNjKGFycl9kZWxheSkpCmBgYAoKIyMjIyAxMzIgbWludXRlIGRlbGF5cwoKLSBBY3R1YWxseSwgQW1lcmljYW4gRWFnbGUgKE1RKSBpc24ndCBzbyBiYWQgYWZ0ZXIgbG9va2luZyBhdCBjYXJyaWVyIHZvbHVtZSAmIGNvbXBhcmluZyB3aXRoIENocmlzJyB0aHJlc2hvbGQKLSBCZXN0IGNhcnJpZXIgcGVyZm9ybWFuY2UKICAgIC0gQW1lcmljYW4gQWlybGluZXMgbG9va3MgYmV0dGVyIHRoYW4gYXZlcmFnZQogICAgLSBVbml0ZWQgbWVldHMgZXhwZWN0YXRpb247IAotIEJlbG93IGF2ZXJhZ2UgY2FycmllciBwZXJmb3JtYW5jZQogICAgLSBQaW5uYWNsZSBBaXJsaW5lcyAoOUUpIAogICAgLSBKZXRCbHVlIChCNikgCgpgYGB7cn0KIyBpbnNwZWN0IGxvbmcgZGVsYXkgdm9sdW1lCk9yZERlbGF5cyAlPiUKICBmaWx0ZXIoIWlzLm5hKGFycl9kZWxheSkpICU+JQogIG11dGF0ZShsb25nX2RlbGF5ID0gYXJyX2RlbGF5ID49IDEzMikgJT4lCiAgdGFsbHkoIH4gbG9uZ19kZWxheSB8IGNhcnJpZXIsIGRhdGEgPSAuLCBmb3JtYXQgPSAiY291bnQiLCBtYXJnaW5zID0gVFJVRSkKCiMgaW5zcGVjdCBsb25nIGRlbGF5IHByb3BvcnRpb24KT3JkRGVsYXlzICU+JQogIGZpbHRlcighaXMubmEoYXJyX2RlbGF5KSkgJT4lCiAgbXV0YXRlKGxvbmdfZGVsYXkgPSBhcnJfZGVsYXkgPj0gMTMyKSAlPiUKICB0YWxseSggfiBsb25nX2RlbGF5IHwgY2FycmllciwgZGF0YSA9IC4sIGZvcm1hdCA9ICJwZXJjZW50IikgJT4lCiAgcm91bmQoMikKCmBgYAoKIyMgV2UgbmVlZCBhIHdheSB0byBhY2NvdW50IGZvciBvdGhlciB2YXJpYWJsZXMgaW4gb3VyIG1vZGVsLi4uCgotIFE6IEFyZSB0aGUgZWZmZWN0cyB3ZSdyZSBvYnNlcnZpbmcgKipyZWFsKiogb3IgYXJlIHdlIGp1c3QgYmVpbmcgZm9vbGVkIGJ5ICoqcmFuZG9tbmVzcyoqPwotIFE6IEhvdyBzaG91bGQgd2UgZXZhbHVhdGU/CiAgICAtIGludGVycHJldAogICAgLSBjb25jbHVkZQogICAgLSB3aGF0IGFib3V0IG1vcmUgZGF0YT8KCmBgYHtyfQojIGZvciBzaW1wbGljaXR5LCB3ZSBtb2RlbCBhdmVyYWdlIGFycml2YWwgZGVsYXlzIGhlcmUKb3JkTW9kZWwgPC0gCiAgT3JkQ29sbGVhZ3VlRGF0YSAlPiUgCiAgbXV0YXRlKGNhcnJpZXJBQSA9IGlmX2Vsc2UoY2FycmllciAlaW4lIGMoIkFBIiksIHRydWUgPSAiQUEiLCBmYWxzZSA9ICJPdGhlckNhcnJpZXIiKSwgCiAgICAgICAgIHNlYXNvbiA9IGlmX2Vsc2UobW9udGggJWluJSAzOjUsIHRydWUgPSAiU3ByaW5nIiwgZmFsc2UgPSAiTm90U3ByaW5nIikpICU+JQogIGxtKGFycl9kZWxheSB+IGNhcnJpZXJBQSArIHNlYXNvbiwgZGF0YSA9IC4pCgojIEhvdyB0byBpbnRlcnByZXQ/IFdoYXQgaGF2ZSB3ZSBsZWFybmVkPyAKbXN1bW1hcnkob3JkTW9kZWwpICAKY29uZmludChvcmRNb2RlbCkgIApgYGAKCjwhLS0gRGF5MyAtLT4KCgojIyBTZWd1ZSB0byBNb2RlbCBCYXNpY3MKCi0gV2Ugd2VyZSB0cnlpbmcgdG8gdXNlIENocmlzJyBmbGlnaHQgZGF0YSB0byBjb21lIHVwIHdpdGggYSBydWxlIHRoYXQgY291bGQgYmUgdXNlZnVsIGZvciBwcmV2ZW50aW5nIGV4Y2Vzc2l2ZSBkZWxheXMgZnJvbSBqZW9wYXJkaXppbmcgaGlzIG1lZXRpbmdzIGluIENoaWNhZ28KICAgIC0gV2Ugc3RhcnRlZCBieSBvbmx5IGxvb2tpbmcgYXQgdGhlIGFycml2YWwgZGVsYXkgaXRzZWxmIGBhcnJfZGVsYXlgCiAgICAtIEFmdGVyIHBva2luZyBhcm91bmQgZm9yIGEgd2hpbGUsIHdlIG5vdGljZWQgdGhhdCB1c2VmdWwgaW5mb3JtYXRpb24gbWlnaHQgYmUgZ2FpbmVkIGZyb20gb3RoZXIgdmFyaWFibGVzCiAgICAgICAgLSBjYXJyaWVyCiAgICAgICAgLSB0aW1lIG9mIHllYXIgKGxvbmdlc3QgZGVsYXlzIGluIFNwcmluZykKCgojIyBNdWx0aXBsZSBUZXN0aW5nCgotIElmIHlvdSBlbmQgdXAgZXZhbHVhdGluZyBtdWx0aXBsZSB0ZXN0cyBvZiB0aGUgc2FtZSBkYXRhLCB5b3UgbG9zZSBjb250cm9sIG9mIFR5cGUgSSBlcnJvciByYXRlCiAgICAtIFE6IFdoYXQgZG9lcyB0eXBlIEkgZXJyb3IgcmF0ZSBtZWFuPwogICAgLSBROiBIb3cgZG8gd2UgcXVhbnRpZnkgdHlwZSBJIGVycm9yIHJhdGU/CiAgICAtIFE6IFdoYXQncyB0aGUgYmlnIGRlYWw/ICBXaHkgZG9lcyBpdCBtYXR0ZXI/PwoKLSBNdWx0aXBsZSB0ZXN0aW5nIGFuZCBhcHByb3ByaWF0ZSBhZGp1c3RtZW50cwoKYGBge3J9CiMgMyBUZXN0cy4uLgoxIC0gKDEgLSAwLjA1KV4zCgojIEJvbmZlcnJvbmkgYWRqdXN0bWVudCBmb3IgMyB0ZXN0cwpiIDwtIDAuMDUgLyAzCjEgLSAoMSAtIGIpXjMKCmBgYAoKIyMgQVNBIFN0YXRlbWVudCBvbiBwLXZhbHVlcy4uLgoKLSBMb3RzIG9mIHBlb3BsZSBoYXZlIGhlYXJkIG9mICJwLXZhbHVlcyIgYW5kIGhhdmUgc29tZSB2YWd1ZSBjb25jZXB0IG9mICJzbWFsbGVyIHRoZSBiZXR0ZXIiCi0gSW5jb21wbGV0ZSB1bmRlcnN0YW5kaW5nIGNhbiBsZWFkIHRvIGJvZ3VzIGNvbmNsdXNpb25zIGFuZCBkaXN0cnVzdCBvZiBzdGF0aXN0aWNpYW5zICYgZGF0YSBhbmFseXN0cwotIEhlcmUncyB3aGF0IHRoZSBBbWVyaWNhbiBTdGF0aXN0aWNhbCBBc3NvYyB3YW50cyBldmVyeW9uZSB0byBrbm93IGFib3V0IHAtdmFsdWVzCiAgICAxLiBwLXZhbHVlcyBjYW4gaW5kaWNhdGUgaG93ICoqaW4qKmNvbXBhdGlibGUgdGhlIGRhdGEgYXJlIHdpdGggYSBzcGVjaWZpZWQgbW9kZWwKICAgIDIuIHAtdmFsdWVzIGRvIE5PVCBtZWFzdXJlIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBzdHVkaWVkIGh5cG90aGVzaXMgaXMgdHJ1ZSwgb3IgdGhlIHByb2JhYmlsaXR5IHRoYXQgdGhlIGRhdGEgd2VyZSBwcm9kdWNlZCBieSByYW5kb20gY2hhbmNlIGFsb25lCiAgICAzLiBzY2llbnRpZmljIGNvbmNsdXNpb25zIGFuZCBidXNpbmVzcyBvciBwb2xpY3kgZGVjaXNpb25zIHNob3VsZCBOT1QgYmUgYmFzZWQgb25seSBvbiB3aGV0aGVyIGEgcC12YWx1ZSBwYXNzZXMgYSBzcGVjaWZpYyB0aHJlc2hvbGQKICAgIDQuIHByb3BlciBpbmZlcmVuY2UgcmVxdWlyZXMgZnVsbCByZXBvcnRpbmcgYW5kIHRyYW5zcGFyZW5jeQogICAgNS4gYSBwLXZhbHVlLCBvciBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UsIGRvZXMgbm90IG1lYXN1cmUgdGhlIHNpemUgb2YgYW4gZWZmZWN0IG9yIHRoZSBpbXBvcnRhbmNlIG9mIGEgcmVzdWx0CiAgICA2LiBieSBpdHNlbGYsIGEgcC12YWx1ZSBkb2VzIG5vdCBwcm92aWRlIGEgZ29vZCBtZWFzdXJlIG9mIGV2aWRlbmNlIHJlZ2FyZGluZyBhIG1vZGVsIGh5cG90aGVzaXMuCgoKIyMgSHlwb3RoZXNpcyBnZW5lcmF0aW9uIHZzLiBoeXBvdGhlc2lzIGNvbmZpcm1hdGlvbiAKCi0gdHJhZGl0aW9uYWwgZm9jdXMgb2YgbW9kZWxsaW5nIGlzIG9uIGluZmVyZW5jZS0tYXR0ZW1wdGluZyB0byBjb25maXJtIHNvbWUgaHlwb3RoZXNpcyBpcyB0cnVlIChvciBub3QgZmFsc2UuLi4pCi0gdGhlIHByb2Nlc3MgaXMgbm90ICpjb21wbGljYXRlZCosIGJ1dCBjYW4gYmUgKmhhcmQqIGJlY2F1c2Ugd2UgbmVlZCB0byB1bmRlcnN0YW5kIHRoYXQKICAgIDEuIEVhY2ggb2JzZXJ2YXRpb24gY2FuIGVpdGhlciBiZSB1c2VkIGZvciBleHBsb3JhdGlvbiBvciBjb25maXJtYXRpb24sIG5vdCBib3RoLgogICAgMi4gWW91IGNhbiB1c2UgYW4gb2JzZXJ2YXRpb24gYXMgbWFueSB0aW1lcyBhcyB5b3UgbGlrZSBmb3IgZXhwbG9yYXRpb24sIGJ1dCBvbmx5IG9uY2UgZm9yIGNvbmZpcm1hdGlvbi4gCi0gKkFzIHNvb24gYXMgeW91IHVzZSBhbiBvYnNlcnZhdGlvbiB0d2ljZSwgeW914oCZdmUgc3dpdGNoZWQgZnJvbSBjb25maXJtYXRpb24gdG8gZXhwbG9yYXRpb24uKiAgSWYgcG9zc2libGUsIGNvbnNpZGVyIHBhcnRpdGlvbmluZyB0aGUgZGF0YSBhcyBmb2xsb3dzOgogICAgLSA2MCUgb2YgdGhlIGRhdGEgZm9yICoqZXhwbG9yYXRpb24qKiAob3IgdHJhaW5pbmcpIHNldAogICAgLSAyMCUgb2YgdGhlIGRhdGEgZm9yIGEgKipxdWVyeSoqIChvciB2YWxpZGF0aW9uKSBzZXQKICAgIC0gMjAlIG9mIHRoZSBkYXRhIGZvciBhICoqdGVzdCoqIChvciBjb25maXJtYXRpb24pIHNldAoKCgojIyBNb2RlbCBCYXNpY3MKCiMjIyMgR29hbHMgb2YgbW9kZWxpbmc6CgotICJwcm92aWRlIGEgc2ltcGxlLCBsb3ctZGltZW5zaW9uYWwgc3VtbWFyeSBvZiBhIGRhdGEgc2V0IiAoUjREUykKLSBZb3UgbWF5IHVzZSB0aGVzZSB0b29scyBpbiBkaWZmZXJlbnQgY29udGV4dHMgZm9yIHZhcmlvdXMgcHVycG9zZXMKICAgIC0gZGVzY3JpcHRpb24KICAgIC0gaW5mZXJlbmNlCiAgICAtIHByZWRpY3Rpb24KLSBJbiBlYWNoIGNhc2UsIHdlIGFyZSAqZXhwbGFpbmluZyB2YXJpYXRpb24qCiAgICAtIHNvbWUgdmFyaWF0aW9uIGluIHRoZSBkYXRhIGlzIHN0cnVjdHVyYWwKICAgIC0gc29tZSB2YXJpYXRpb24gaW4gdGhlIGRhdGEgaXMgc2ltcGx5IHJhbmRvbW5lc3MKCiMjIENob29zaW5nIGEgbW9kZWwKCi0gTW9kZWwgZmFtaWx5IChnZW5lcmFsKQogICAgLSBDaG9zZW4gYmFzZWQgb24gdW5kZXJzdGFuZGluZyBvZiBkYXRhIGFuZCBnb2FscyBvZiBhbmFseXNpcyAKICAgIC0gRXhwcmVzc2VzIGEgcHJlY2lzZSAoYnV0IGdlbmVyaWMpIHBhdHRlcm4geW91IHdhbnQgdG8gY2FwdHVyZQogICAgICAgIC0gTGluZTogJHkgPSBhXzEgKyBhXzIgKiB4JAogICAgICAgIC0gQ3VydmU6ICR5ID0gYV8xICogeF57YV8yfSQKICAgICAgICAtICR5JCAmICR4JCBhcmUgKip2YXJpYWJsZXMqKiB0aGF0IGFyZSAqKm9ic2VydmVkKiogaW4geW91ciBkYXRhCiAgICAgICAgLSAkYV8xJCAmICRhXzIkIGFyZSAqKnBhcmFtZXRlcnMqKiB0aGF0IGNhbiBiZSBtb2RpZmllZCB0byBjYXB0dXJlIGRpZmZlcmVudCBwYXR0ZXJucwotIEZpdHRlZCBtb2RlbCAoc3BlY2lmaWMpCiAgICAtICoqQ29uc3RyYWluZWQgYnkgbW9kZWwgZmFtaWx5KioKICAgIC0gQ2hvb3NlIHBhcmFtZXRlcnMgdGhhdCByZXN1bHQgaW4gdGhlIG1vZGVsIHRoYXQgaXMgImNsb3Nlc3QiIHRvIHlvdXIgZGF0YQogICAgICAgIC0gTGluZTogJHkgPSA3ICsgMyAqIHgkIAogICAgICAgIC0gQ3VydmU6ICR5ID0gOSAqIHheMiQgCgojIyBBbGwgbW9kZWxzIGFyZSB3cm9uZyAoc29tZSBhcmUgdXNlZnVsKQotICJBbGwgbW9kZWxzIGFyZSB3cm9uZywgc29tZSBhcmUgdXNlZnVsIiAtLUdlb3JnZSBCb3ggKHN0YXRpc3RpY2lhbikKLSBDbGFzc2ljYWwgTWVjaGFuaWNzIChwaHlzaWNzKTogCiAgICAtIElzYWFjIE5ld3RvbidzICJsYXdzIiBvZiBtb3Rpb24gKHBoeXNpY3MpIGFyZSBkZW1vbnN0cmFibHkgKip3cm9uZyoqCiAgICAgICAgLSBUaGUgbW9kZWwgZ2VuZXJhbGx5IGRvZXMgd2VsbCBkZXNjcmliaW5nIG1vdGlvbiBmb3Igb2JqZWN0cyBhcyBzbWFsbCBhcyBiYWN0ZXJpYSBhbmQgYXMgbGFyZ2UgYXMgcGxhbmV0cywgc3RhcnMsIGFuZCBnYWxheGllcy4gIAogICAgICAgIC0gTmV3dG9uJ3MgbGF3cyB3ZXJlIGtub3duIHRvIGJlIGluYWNjdXJhdGUgd2hlbiBwcmVkaWN0aW5nIHRoZSBvcmJpdCBvZiBNZXJjdXJ5CiAgICAtIENsb3NlIGVub3VnaD8hIChpLmUuICoqdXNlZnVsKio/KQotIEVpbnN0ZWluJ3MgdGhlb3J5IG9mIChnZW5lcmFsKSByZWxhdGl2aXR5CiAgICAtIENsYXNzaWNhbCBtZWNoYW5pY3MgYnJlYWtzIGRvd24gd2hlbiBib2RpZXMgaW52b2x2ZWQgYXJlIGV4dHJlbWVseSBtYXNzaXZlIG9yIG1vdmluZyBleHRyZW1lbHkgZmFzdAogICAgLSBBY2N1cmF0ZSBHUFMgcmVxdWlyZXMgcmVsYXRpdml0eSAoTmV3dG9uIHdvdWxkIGJlIG9mZiBieSBhIGNvdXBsZSBtaWxlcykKCiMjIyMgTW9yYWw6IGFsbCBtb2RlbHMgYXJlIHdyb25nOyBzb21lIGFyZSB1c2VmdWwKCjEuIE5ld3RvbmlhbiBwaHlzaWNzIHdhcyBrbm93biB0byBiZSAqKndyb25nKiogZm9yIG5lYXJseSAyIGNlbnR1cmllcyBiZWZvcmUgcmVsYXRpdml0eSBjYW1lIGFsb25nLCBpbiBwYXJ0IGJlY2F1c2UgaXQgZmFpbGVkIHRvIGFjY3VyYXRlbHkgcHJlZGljdCB0aGUgb3JiaXQgb2YgTWVyY3VyeS4gIFRoZSBtb2RlbCB3YXMgKGFuZCBpcz8pICoqdXNlZnVsKiogZm9yIHRvbnMgb2YgcHJhY3RpY2FsIHB1cnBvc2VzCgoyLiBFaW5zdGVpbidzIHRoZW9yeSBvZiByZWxhdGl2aXR5IGlzIGFsbW9zdCBjZXJ0YWlubHkgKip3cm9uZyoqIHRvbyBiZWNhdXNlIGl0IGZhaWxzIGZvciBzdWJhdG9taWMgcGFydGljbGVzLi4uIGJ1dCBpdCdzIHN0aWxsICoqdXNlZnVsKiogCgoKIyMgU2ltcGxlIGV4YW1wbGUKCi0gV2UnbGwgdXNlIGxpbmVhciByZWdyZXNzaW9uIGFzIGEgc2FuZGJveCB0byBidWlsZCBzb21lIGludHVpdGlvbiBhbmQgdG9vbHMKICAgIC0gb25jZSB5b3UgdW5kZXJzdGFuZCBzb21lIGJhc2ljIHByaW5jaXBsZXMgaW4gdGhlIGNvbnRleHQgb2YgcmVncmVzc2lvbiwgd2UgY2FuIHRyYW5zbGF0ZSB0byBvdGhlciBtb2RlbHMKLSBjb25zaWRlciBhIHNpbXVsYXRlZCBkYXRhIHNldCBgc2ltMWAgd2l0aCB2YXJpYWJsZXMgYHlgIGFuZCBgeGAKLSBpdCBsb29rcyBsaWtlIHRoZXkncmUgcmVsYXRlZCB0byBvbmUgYW5vdGhlciwgc28gcGVyaGFwcyB3ZSBjYW4gbW9kZWwgdGhlaXIgcmVsYXRpb25zaXAKICAgIC0gRmFtaWx5OiAkeSA9IGJfMCArIGJfMSAqIHgkCiAgICAtIEZpdDogY2hvb3NlIHRoZSAiYmVzdCIgJGJfMCQgYW5kICRiXzEkCgpgYGB7cn0Kc2ltMSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB4LCB5ID0geSkpICsgCiAgZ2VvbV9wb2ludCgpCmBgYAoKIyMgU2ltcGxlIGV4YW1wbGUgKHJhbmRvbSBtb2RlbHMpCgotIHdlIGNvdWxkIGp1c3QgcmFuZG9tbHkgZ2VuZXJhdGUgbW9kZWxzIGFuZCBwaWNrIGEgZmV3IGNhbmRpZGF0ZXMgdGhhdCBsb29rICJiZXN0IgotIGludHVpdGlvbiBzYXlzIHRoaXMgaXMgc2lsbHkgYXBwcm9hY2guLi4gCiAgICAtIFdoeSBkbyB3ZSB0aGluayBrbm93IGJldHRlcj8KICAgIC0gSG93IG1pZ2h0IHdlIGp1ZGdlIHdoZXRoZXIgb25lIG1vZGVsIGlzICJiZXR0ZXIiIHRoYW4gYW5vdGhlcj8KCgpgYGB7cn0KbW9kZWxzIDwtIAogIHRpYmJsZSgKICBiMCA9IHJ1bmlmKDI1MCwgLTIwLCA0MCksCiAgYjEgPSBydW5pZigyNTAsIC01LCA1KQogICkKCnNpbTEgJT4lIAogIGdncGxvdChhZXMoeCwgeSkpICsgCiAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IGIwLCBzbG9wZSA9IGIxKSwgZGF0YSA9IG1vZGVscywgYWxwaGEgPSAwLjI1KSArIAogIGdlb21fcG9pbnQoKSAKYGBgCgoKIyMgRGlzdGFuY2UgZnJvbSBwb2ludHMgdG8gdGhlIG1vZGVsICgxLzQpCgotIGEgbmF0dXJhbCB0aG91Z2h0IGlzIHRvIGV2YWx1YXRlIHRoZSB2ZXJ0aWNhbCBkaXN0YW5jZSBmcm9tIGVhY2ggcG9pbnQgdG8gYSBjYW5kaWRhdGUgbW9kZWwKICAgIC0gdGhlIHBvaW50IHJlcHJlc2VudHMgb3VyIG9ic2VydmVkICoqcmVzcG9uc2UqKiAoZnJvbSB0aGUgZGF0YSkKICAgIC0gdGhlIGNvcnJlc3BvbmRpbmcgbG9jYXRpb24gb24gdGhlIG1vZGVsIGlzIGEgKipwcmVkaWN0aW9uKioKLSBtb2RlbCBmaXR0aW5nIGFwcGxldDogPGh0dHA6Ly93d3cucm9zc21hbmNoYW5jZS5jb20vYXBwbGV0cy9SZWdTaHVmZmxlLmh0bT4KLSBOb3RlOiB4LXZhbHVlcyBhcmUgc2hpZnRlZCBzbGlnaHRseSB0byBhdm9pZCBvdmVycGxvdHRpbmcgaW4gdGhlIGZpZ3VyZQoKIVtdKGFic0Vycm9yc1I0RFMucG5nKQoKCiMjIERpc3RhbmNlIGZyb20gcG9pbnRzIHRvIHRoZSBtb2RlbCAoMi80KQoKLSBjYWxjdWxhdGluZyB0aGUgZGlzdGFuY2UgZGlyZWN0bHkKICAgIC0gbGV0J3MgdHVybiBvdXIgbW9kZWwgaW50byBhIGZ1bmN0aW9uCgpgYGB7cn0KbW9kZWwxIDwtIGZ1bmN0aW9uKGEsIGRhdGEpIHsKICAjIHB1cnBvc2U6IGNhbGN1bGF0ZSBwcmVkaWN0aW9ucyBmb3IgYSBnaXZlbiBtb2RlbCAKICAjIGlucHV0czogCiAgIyMjIGE6IHZlY3RvciBvZiBjb2VmZmljaWVudCBlc3RpbWF0ZXMgZm9yIGxpbmVhciBtb2RlbCAoZS5nLiBpbnRlcmNlcHQgJiBzbG9wZXMpCiAgIyMjIGRhdGE6IHZlY3RvciBvZiBvYnNlcnZlZCByZXNwb25zZSBkYXRhCiAgCiAgYVsxXSArIGRhdGEkeCAqIGFbMl0KfQoKIyBzaG93IG1vZGVsIHByZWRpY3Rpb25zCm1vZGVsMShjKDcsIDEuNSksIHNpbTEpCmBgYAoKIyMgRGlzdGFuY2UgZnJvbSBwb2ludHMgdG8gdGhlIG1vZGVsICgzLzQpCgotIGNhbGN1bGF0aW5nIHRoZSBkaXN0YW5jZSBkaXJlY3RseQogICAgLSBsZXQncyB0dXJuIG91ciBtb2RlbCBpbnRvIGEgZnVuY3Rpb24KICAgIC0gbWVhc3VyZSBjb2xsZWN0aXZlIGRpc3RhbmNlIGZyb20gKiphbGwgcG9pbnRzKiogdG8gdGhlIG1vZGVsCiAgICAgICAgLSBTdGF0aXN0aWNpYW5zIHR5cGljYWxseSB1c2UgdGhlIOKAnHJvb3QtbWVhbi1zcXVhcmVkIiBkZXZpYXRpb24KCmBgYHtyfQptZWFzdXJlX2Rpc3RhbmNlIDwtIGZ1bmN0aW9uKG1vZCwgZGF0YSkgewogICMgcHVycG9zZTogY2FsY3VsYXRlIGRpc3RhbmNlIGZyb20gb2JzZXJ2ZWQgcG9pbnRzIHRvIG1vZGVsIHByZWRpY3Rpb25zCiAgIyBpbnB1dHM6IAogICMjIyBtb2Q6IHZlY3RvciBvZiBwYXJhbWV0ZXIgZXN0aW1hdGVzIGZvciBsaW5lYXIgbW9kZWwgKGUuZy4gaW50ZXJjZXB0ICYgc2xvcGUocykpCiAgIyMjIGRhdGE6IG9ic2VydmVkIGRhdGEgCiAgCiAgZGlmZiA8LSBkYXRhJHkgLSBtb2RlbDEobW9kLCBkYXRhKQogIHNxcnQobWVhbihkaWZmIF4gMikpCn0KCiMgc2hvdyBSTVMgZGV2aWF0aW9uIGZvciB0aGUgbW9kZWwKbWVhc3VyZV9kaXN0YW5jZShjKDcsIDEuNSksIHNpbTEpCmBgYAoKIyMgRGlzdGFuY2UgZnJvbSBwb2ludHMgdG8gZWFjaCBjYW5kaWRhdGUgbW9kZWxzICg0LzQpCgotIGNhbGN1bGF0aW5nIHRoZSBkaXN0YW5jZSBkaXJlY3RseQogICAgLSBsZXQncyB0dXJuIG91ciBtb2RlbCBpbnRvIGEgZnVuY3Rpb24KICAgIC0gbWVhc3VyZSBjb2xsZWN0aXZlIGRpc3RhbmNlIGZyb20gKiphbGwgcG9pbnRzKiogdG8gdGhlIG1vZGVsCi0gcmVwZWF0IGZvciBhbGwgMjUwIG9mIG91ciBtb2RlbHMgdXNpbmcgYHB1cnJyOjptYXAyKClgIAoKYGBge3J9CnNpbTFfZGlzdCA8LSBmdW5jdGlvbihiMCwgYjEpIHsKICAjIHB1cnBvc2U6IGhlbHBlciBmdW5jdGlvbiBiZWNhdXNlIG91ciBkaXN0YW5jZSBmdW5jdGlvbiAKICAjICAgICAgICAgICAgZXhwZWN0cyB0aGUgbW9kZWwgYXMgYSBudW1lcmljIHZlY3RvciBvZiBsZW5ndGggMgogICMgaW5wdXRzOiAKICAjIyMgYjA6IGludGVyY2VwdCBlc3RpbWF0ZQogICMjIyBiMTogc2xvcGUgZXN0aW1hdGUgCiAgIyMjIHNpbTE6IGRhdGEgaW4gZW52aXJvbm1lbnQKCiAgbWVhc3VyZV9kaXN0YW5jZShjKGIwLCBiMSksIHNpbTEpCn0KCm1vZGVscyA8LSAKICBtb2RlbHMgJT4lIAogIG11dGF0ZShkaXN0ID0gcHVycnI6Om1hcDJfZGJsKGIwLCBiMSwgLmYgPSBzaW0xX2Rpc3QpKQoKbW9kZWxzCmBgYAoKIyMjIyBQcm9ncmVzcy4uLgoKLSBXZSBub3cgaGF2ZSBhIGRpc3RhbmNlIG1ldHJpYyB0aGF0IHF1YW50aWZpZXMgcGVyZm9ybWFuY2Ugb2YgYWxsIDI1MCBvZiBvdXIgcmFuZG9tIG1vZGVscyAKLSBOb3cgd2UgY2FuIHN0YXJ0IHRvIGV2YWx1YXRlIGlmIHNvbWUgb2YgdGhlbSBhcmUgKip1c2VmdWwqKgoKYGBge3J9CnNpbTEgJT4lIAogIGdncGxvdChhZXMoeCwgeSkpICsgCiAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IGIwLCBzbG9wZSA9IGIxKSwgZGF0YSA9IG1vZGVscywgYWxwaGEgPSAwLjI1KSArIAogIGdlb21fcG9pbnQoKSAKYGBgCgoKCiMjIEJlc3QgKHJhbmRvbSkgbW9kZWxzIGZvciBgc2ltMWAgZGF0YQoKLSB3ZSBoYXZlIGEgbWV0cmljCi0gbGV0J3MgaW5zcGVjdCBvdXIgdG9wIDEwIHJlc3VsdHMgYWNjb3JkaW5nIHRvIG91ciBkaXN0YW5jZSBjcml0ZXJpb24KICAgIC0gYnJpZ2h0ZXN0IG1vZGVscyBmaXQgdGhlIGRhdGEgImJlc3QiCiAgICAtIG5vdGUgYWxsIHRoZSBvYnZpb3VzbHkgc2lsbHkgbW9kZWxzIGFyZSBnb25lCiAgICAtIHNldmVyYWwgZGVjZW50IGNhbmRpZGF0ZSBtb2RlbHMgbGVmdAotIFE6IEhvdyBjYW4gd2UgZG8gKmV2ZW4gYmV0dGVyKj8KCgpgYGB7cn0Kc2ltMSAlPiUKICBnZ3Bsb3QoYWVzKHgsIHkpKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGNvbG91ciA9ICJncmV5MzAiKSArIAogIGdlb21fYWJsaW5lKGFlcyhpbnRlcmNlcHQgPSBiMCwgc2xvcGUgPSBiMSwgY29sb3VyID0gLWRpc3QpLCAKICAgICAgICAgICAgICBkYXRhID0gZmlsdGVyKG1vZGVscywgcmFuayhkaXN0KSA8PSAxMCkpCmBgYAoKPCEtLSBCZWdpbiBEYXkgNCAoMS8zMCkgLS0+CgojIyBCZXN0IChyYW5kb20pIG1vZGVscyBmb3IgYHNpbTFgIGRhdGEKCi0gV2hhdCBpZiB3ZSB2aXN1YWxpemVkIHRoZSBzbG9wZSAoYjEpLCBpbnRlcmNlcHQgKGIwKSwgYW5kIGRpc3RhbmNlIG1ldHJpYyAoZGlmZikgb24gYSAzLWRpbWVuc2lvbmFsIHBsb3Q/CiAgICAtIHNsb3BlIGFuZCBpbnRlcmNlcHQgYXJlICoqYm90aCoqIHJlbGF0ZWQgdG8gZGlzdGFuY2UgbWV0cmljCiAgICAtIHdlIGp1c3Qgd2FudCB0byBzZWFyY2ggZm9yIHRoZSAiYmVzdCIgY29tYmluYXRpb25zIHVzaW5nIG91ciBjcml0ZXJpb24KLSBwb2ludHMgYXJlIHNsb3BlL2ludGVyY2VwdCBwYWlycyAoaS5lLiBtb2RlbHMpIGNvbG9yZWQgYnkgb3VyIGRpc3RhbmNlIGNyaXRlcmlvbiAKICAgIC0gYnJpZ2h0ZXIgY29sb3IgaXMgImJldHRlciIgKGkuZS4gbGVzcyBjb2xsZWN0aXZlIGRpc3RhbmNlIGZyb20gbW9kZWwgdG8gZGF0YSkKICAgIC0gdGhlIHRvcCAxMCBzaG93biBwcmV2aW91c2x5IGFyZSBoaWdobGlnaHRlZCBpbiByZWQKCmBgYHtyfQpnZ3Bsb3QobW9kZWxzLCBhZXMoYjAsIGIxKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGZpbHRlcihtb2RlbHMsIHJhbmsoZGlzdCkgPD0gMTApLCBzaXplID0gNCwgY29sb3VyID0gInJlZCIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSAtZGlzdCkpCmBgYAoKIyMgUmFuZG9tIG1vZGVscyB3YXMgc2lsbHkuLi4gYnV0IG5vdCAqdGhhdCogc2lsbHkKCi0gVHVybnMgb3V0IHdlIGNvdWxkIGxlYXJuIHF1aXRlIGEgYml0IHdpdGggdGhpcyBtZXRob2QsIGFuZCBjYW1lIHVwIHdpdGggYSBoYW5kZnVsIG9mIGRlY2VudCBtb2RlbHMKLSBROiBIb3cgbWlnaHQgd2UgZXh0ZW5kIHRoZSBtZXRob2QgZm9yIGEgbW9yZSBzeXN0ZW1hdGljIGFwcHJvYWNoPwoKCgoKIyMgU3lzdGVtYXRpYyBzZWFyY2gKCi0gTWF5YmUgaW5zdGVhZCBvZiBzZWFyY2hpbmcgcmFuZG9tbHksIHdlIHNlYXJjaCAqc3lzdGVtYXRpY2FsbHkqIHdpdGggYSBncmlkIG9mIHNsb3BlLWludGVyY2VwdCBjb21iaW5hdGlvbnM/CiAgICAtIHdlJ2xsIHN0YXJ0IHdpdGggMjUwIG1vZGVscyBhZ2FpbgogICAgLSB3ZSBjYW4gY29udHJvbCBob3cgZmluZSB0aGUgZ3JpZCBpcyBpZiB3ZSB3YW50CiAgICAtIGF2b2lkIGNsdW1waW5nIGFuZCBzcGFyc2l0eSBvZiByYW5kb21uZXNzCi0gc28gZmFyIHRoZSBuZXcgZ3JpZCBzdWdnZXN0cwogICAgLSBiMCBpcyBtYXliZSBhcm91bmQgNAogICAgLSBiMSBpcyBtYXliZSBhcm91bmQgMgogICAgLSBROiBJcyB0aGUgbW9kZWwgJHkgPSA0ICsgMlgkIHdyb25nPyBJcyBpdCB1c2VmdWw/ICAKCmBgYHtyfQojIGNyZWF0ZSBzeXN0ZW1hdGljIGdyaWQ7IGZpdCBhbGwgbW9kZWxzIHRvIHRoZSBkYXRhCmdyaWQgPC0gCiAgZXhwYW5kLmdyaWQoYjAgPSBzZXEoLTUsIDIwLCBsZW5ndGggPSAyNSksIAogICAgICAgICAgICAgIGIxID0gc2VxKDEsIDMsIGxlbmd0aCA9IDI1KSkgJT4lIAogIG11dGF0ZShkaXN0ID0gcHVycnI6Om1hcDJfZGJsKGIwLCBiMSwgc2ltMV9kaXN0KSkKCiMgcGxvdCBtb2RlbHMgYXMgcG9pbnRzOyBjb2xvciAiYmVzdCIKZ3JpZCAlPiUgCiAgZ2dwbG90KGFlcyhiMCwgYjEpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZmlsdGVyKGdyaWQsIHJhbmsoZGlzdCkgPD0gMTApLCBzaXplID0gNCwgY29sb3VyID0gInJlZCIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSAtZGlzdCkpIAoKc2ltMSAlPiUKICBnZ3Bsb3QoYWVzKHgsIHkpKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGNvbG91ciA9ICJncmV5MzAiKSArIAogIGdlb21fYWJsaW5lKGFlcyhpbnRlcmNlcHQgPSBiMCwgc2xvcGUgPSBiMSwgY29sb3VyID0gLWRpc3QpLCAKICAgICAgICAgICAgICBkYXRhID0gZmlsdGVyKGdyaWQsIHJhbmsoZGlzdCkgPD0gMTApKQpgYGAKCgojIyBTeXN0ZW1hdGljIHNlYXJjaCBjb250J2QKCi0gbGV0J3MgdHJ5IGEgZmluZXIgZ3JpZC4uLgotIGJlc3QgcmVzdWx0cyBzbyBmYXIgCiAgICAtIGIwIGJldHdlZW4gMCBhbmQgNzsgCiAgICAtIGIxIGJldHdlZW4gMS41IGFuZCAyLjUKLSBROiBjb3VsZCB3ZSBqdXN0IGNvbnRpbnVlIHRoaXMgd2F5PwoKCmBgYHtyfQojIGNyZWF0ZSBzeXN0ZW1hdGljIGdyaWQ7IGZpdCBhbGwgbW9kZWxzIHRvIHRoZSBkYXRhCmdyaWQgPC0gCiAgZXhwYW5kLmdyaWQoYjAgPSBzZXEoMCwgNywgbGVuZ3RoID0gNTApLCAKICAgICAgICAgICAgICBiMSA9IHNlcSgxLjUsIDIuNSwgbGVuZ3RoID0gNTApKSAlPiUgCiAgbXV0YXRlKGRpc3QgPSBwdXJycjo6bWFwMl9kYmwoYjAsIGIxLCBzaW0xX2Rpc3QpKQoKIyBwbG90IG1vZGVscyBhcyBwb2ludHM7IGNvbG9yICJiZXN0IgpncmlkICU+JSAKICBnZ3Bsb3QoYWVzKGIwLCBiMSkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBmaWx0ZXIoZ3JpZCwgcmFuayhkaXN0KSA8PSAxMCksIHNpemUgPSA0LCBjb2xvdXIgPSAicmVkIikgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IC1kaXN0KSkgCgpzaW0xICU+JQogIGdncGxvdChhZXMoeCwgeSkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMiwgY29sb3VyID0gImdyZXkzMCIpICsgCiAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IGIwLCBzbG9wZSA9IGIxLCBjb2xvdXIgPSAtZGlzdCksIAogICAgICAgICAgICAgIGRhdGEgPSBmaWx0ZXIoZ3JpZCwgcmFuayhkaXN0KSA8PSAxMCkpCmBgYAoKIyMgUmVjYXAgb2Ygb3VyIGdyaWQgc2VhcmNoLi4uCgotIFdlIGRlZmluZWQgYSBkaXN0YW5jZSBtZXRyaWMgKHJvb3QtbWVhbi1zcXVhcmUgZGV2aWF0aW9uKSB0aGF0IHdlIHdhbnQgdG8gdXNlIHRvIGNvbXBhcmUgbW9kZWxzCiAgICAtIFNtYWxsZXIgUk1TIGRldmlhdGlvbiBpbmRpY2F0ZXMgdGhlIG1vZGVsIGlzICJjbG9zZSIgdG8gdGhlIGRhdGEgCiAgICAtIFRoZSAiYmVzdCIgbW9kZWwgaXMgdGhlIG9uZSB0aGF0IG1pbmltaXplcyBvdXIgY3JpdGVyaWFuIChpLmUuIFJNUyBkZXZpYXRpb24pCi0gU2VhcmNoIHByb2Nlc3M6CiAgICAxLiBkZWNpZGVkIG9uIGEgbW9kZWwgZmFtaWx5IChsaW5lYXIgaW4gdGhpcyBjYXNlKQogICAgMi4gV2Ugc3RhcnRlZCB3aXRoIGEgYnVuY2ggb2YgcmFuZG9tIGxpbmVzLCBidXQgdGhlbiBkZWNpZGVkIGEgc3lzdGVtYXRpYyBncmlkIG9mIGNvbWJpbmF0aW9ucyB3YXMgYSBiZXR0ZXIgaWRlYQogICAgMy4gV2UgcGlja2VkIHRoZSAxMCBiZXN0IG1vZGVscyB1c2luZyBvdXIgY3JpdGVyaW9uCiAgICA0LiBXZSB0aGVuIHNlYXJjaGVkIGFnYWluIHdpdGggYSBmaW5lciBncmlkCiAgICA1LiBSZXBlYXQgc3RlcHMgNCAmIDUgdW50aWwgc2F0aXNmaWVkCiAgICA2LiBFc3RpbWF0ZSBtb2RlbCBwYXJhbWV0ZXJzCgoKCiMjIGBvcHRpbSgpYCBhbmQgdGhlIE5ld3Rvbi1SYXBoc29uIGFscm9naXRobQoKLSBUaGUgTmV3dG9uLVJhcGhzb24gYWxnb3JpdGhtIGlzIGEgbnVtZXJpY2FsIG9wdGltaXphdGlvbiBzZWFyY2gKICAgIDEuIHBpY2sgYSBzdGFydGluZyBwb2ludCBmb3IgeW91ciBwYXJhbWV0ZXIgZXN0aW1hdGVzCiAgICAyLiBsb29rIGFyb3VuZCBmb3Igc3RlZXBlc3Qgc2xvcGUgKGkuZS4gZ3JlYXRlc3QgaW1wcm92ZW1lbnQgYWdhaW5zdCBvdXIgbWV0cmljKQogICAgMy4gc2tpIGRvd24gdGhhdCBzbG9wZSBhIGJpdAogICAgNC4gcmVwZWF0CiAgICA1LiB5b3UncmUgZG9uZSB3aGVuIHlvdSBjYW4ndCBnZXQgYW55IGxvd2VyCi0gdGhlIGBvcHRpbSgpYCBmdW5jdGlvbiBkb2VzIHNvbWV0aGluZyBjb25jZXB0dWFsbHkgc2ltaWxhciBmb3IgdXMKICAgIC0gd2UgcHJvdmlkZSBzdGFydGluZyBwb2ludDogYGMoMCwgMClgCiAgICAtIHdlIHByb3ZpZGUgZnVuY3Rpb24gdG8gZGVmaW5lIGRpc3RhbmNlIGJldHdlZW4gbW9kZWwgYW5kIGRhdGE6IGBtZWFzdXJlX2Rpc3RhbmNlYAogICAgLSB3ZSBwcm92aWRlIGRhdGE6IGBzaW0xYCAKICAgIC0gUiBkb2VzIHRoZSByZXN0IChub3RlOiBvdXIgZ3JpZCBzZWFyY2ggd2Fzbid0IHNvIGJhZCEhKQotIFE6IGhvdyBtaWdodCB0aGlzIGFsZ29yaXRobSBwb3NzaWJseSByZXN1bHQgaW4gYSBtaXNsZWFkaW5nIGNvbmNsdXNpb24/IAogICAgLSBUaGlzIGRvZXNuJ3QgdXN1YWxseSBoYXBwZW4sIGJ1dCB5b3UgbmVlZCB0byBrbm93IGl0J3MgYSByaXNrCiAgICAtIFE6IElkZWFzIHRvIHByb3RlY3QgYWdhaW5zdCB0aGlzIHJpc2s/CgpgYGB7cn0KbnJCZXN0IDwtIG9wdGltKHBhciA9IGMoMCwgMCksIGZuID0gbWVhc3VyZV9kaXN0YW5jZSwgZGF0YSA9IHNpbTEpCgojIHNob3cgcGFyYW1ldGVyIGVzdGltYXRlcwpuckJlc3QkcGFyCmBgYAoKIyMgYGxtKClgIGZvciBsaW5lYXIgbW9kZWxzCgotIFdlIGhhZCBjaG9zZW4gImxpbmVhciBtb2RlbCIgYXMgb3VyIG1vZGVsIGZhbWlseQotIFRoZSBgbG0oKWAgZnVuY3Rpb24gaW4gUiBpcyB2ZXJ5IGVmZmljaWVudCBmb3IgdGhlIGxpbmVhciBtb2RlbCBmYW1pbHkKICAgIC0gZmFtaWx5OiAkeSA9IGJfMCArIGJfMSAqIHhfMSArIGJfMiAqIHhfMiArIGJfMyAqIHhfMyArIC4uLiQKICAgIC0gUiBzeW50YXg6IGBsbShZIH4gWDEgKyBYMiArIFgzLCBkYXRhID0gRGF0YVNldClgCiAgICAtIGBsbSgpYCBldmVuIGZpeGVzIG91ciBwcm9ibGVtIHdpdGggYG9wdGltKClgIHRvIGd1YXJhbnRlZSBkZWxpdmVyeSBvZiBnbG9iYWwgbWluaW11bQotIFE6IHRob3VnaHRzIGFib3V0IG91ciB0aHJlZSBhcHByb2FjaGVzPwogICAgLSByYW5kb20vZ3JpZCBzZWFyY2gKICAgIC0gTmV3dG9uLVJhcGhzb24gd2l0aCBgb3B0aW0oKWAKICAgIC0gYGxtKClgCiAgICAtIChoYXZpbmcgZnVuPyB0YWtlIFNUQVQgNDQwISkKCmBgYHtyfQpzaW0xX2xtIDwtIGxtKHkgfiB4LCBkYXRhID0gc2ltMSkKc2ltMV9sbSRjb2VmZmljaWVudHMKCmBgYAoKCiMjIFF1YW50aWZ5aW5nIHVuY2VydGFpbnR5IG9mIG91ciBlc3RpbWF0ZXMKCi0gV2Ugbm93IGhhdmUgcG9pbnQgZXN0aW1hdGVzLCBidXQgdGhhdCBkb2VzIGxpdHRsZSBmb3IgdXMgdW5sZXNzIHdlIHVuZGVyc3RhbmQgdGhlICoqdW5jZXJ0YWludHkqKiBvZiB0aG9zZSBlc3RpbWF0ZXMgKGUuZy4gc3RhbmRhcmQgZXJyb3IpCiAgICAtIGBsbSgpYCBoYXMgdGhpcyBmdW5jdGlvbmFsaXR5IGJ1aWx0IGluIGZvciB1cyAKICAgIC0gYGNvbmZpbnQoKWAgaXMgYWxzbyB2ZXJ5IHVzZWZ1bCBmb3IgaW50ZXJ2YWwgZXN0aW1hdGVzIAogICAgLSBROiBob3cgY291bGQgd2UgYWNoaWV2ZSBzb21ldGhpbmcgc2ltaWxhciB1c2luZyBgb3B0aW0oKWA/CgpgYGB7cn0KIyBjb25maWRlbmNlIGludGVydmFscwpjb25maW50KGxtKHkgfiAxLCBkYXRhID0gc2ltMSkpICAjIHdoYXQgZG9lcyB0aGlzIGRvPwpjb25maW50KHNpbTFfbG0sIGxldmVsID0gMC45NSkgICAjIHdoYXQgZG9lcyB0aGlzIGRvPwoKIyBtb2RlbCBzdW1tYXJ5IHRhYmxlCm1zdW1tYXJ5KHNpbTFfbG0pCmBgYAoKIyMgUXVhbnRpZnlpbmcgdW5jZXJ0YWludHkgb2Ygb3VyIGVzdGltYXRlcwoKLSBNdWx0aXBsZSBhcHByb2FjaGVzLi4uCi0gYm9vdHN0cmFwcGluZyAKICAgIC0gc2FtcGxlIHdpdGggcmVwbGFjZW1lbnQgZnJvbSBvdXIgZGF0YQogICAgLSBjb21wdXRlIHRoZSBlc3RpbWF0ZXMgZm9yIGVhY2ggYm9vdHN0cmFwIHNhbXBsZQogICAgLSBjYWxjdWxhdGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIG91ciBib290c3RyYXAgZXN0aW1hdGVzIHRvIGFwcHJveGltYXRlIHRoZSBzdGFuZGFyZCBlcnJvcgotIGBsbSgpYCB0YWtlcyBhZHZhbnRhZ2Ugb2Ygc29tZSBuaWNlIG1hdGhlbWF0aWNhbCBhcHByb3hpbWF0aW9ucwogICAgLSBJRiB0aGUgZGF0YSBjb25mb3JtIHRvIHRoZSBhc3N1bXB0aW9ucyByZXF1aXJlZCBmb3IgdGhvc2UgbWF0aGVtYXRpY2FsIGFwcHJveGltYXRpb25zLCAKICAgICAgICAtIGBsbSgpYCBpcyBjb252ZW5pZW50LCBhY2N1cmF0ZSwgYW5kIGNvbW1vbmx5IHVzZWQKICAgICAgICAtIHRoZSBib290c3RyYXAgc2hvdWxkIGRlbGl2ZXIgc2ltaWxhciByZXN1bHRzLCB0aG91Z2gKICAgIC0gSUYgdGhlIGRhdGEgZG8gTk9UIGNvbmZvcm0gdG8gdGhlIGFzc3VtcHRpb25zLCAKICAgICAgICAtIHlvdSBtYXkgbmVlZCBzb21lIG1vcmUgYWR2YW5jZWQgc2tpbGxzIHRvIHVzZSBgbG0oKWAgcmVzcG9uc2libHkgKHRha2UgU1RBVCA0NjIpCiAgICAgICAgLSB5b3Ugc2hvdWxkIHRoaW5rIGFib3V0IGEgZGlmZmVyZW50IG1vZGVsIGZhbWlseQogICAgICAgIC0gYm9vdHN0cmFwcGluZyBtYXkgb3IgbWF5IG5vdCBiZSB1c2VmdWwgZGVwZW5kaW5nIG9uIHRoZSBpc3N1ZXMgYXQgc3Rha2UKCgpgYGB7cn0KbnJCZXN0IDwtIG9wdGltKHBhciA9IGMoMCwgMCksIGZuID0gbWVhc3VyZV9kaXN0YW5jZSwgZGF0YSA9IHNpbTEpCm5yQmVzdCRwYXIKCiMgbm90ZSB0aGUgYCRwYXJgIGF0IGVuZCBvZiBgb3B0aW0oKWAuLi4gdHJ5IHdpdGhvdXQgdG8gc2VlIHdoeQpCb290c3RyYXBNb2RlbHMgPC0gCiAgbW9zYWljOjpkbygxMDAwKSAqIG9wdGltKHBhciA9IGMoMCwgMCksIGZuID0gbWVhc3VyZV9kaXN0YW5jZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBzYW1wbGVfbihzaW0xLCBzaXplID0gbnJvdyhzaW0xKSwgcmVwbGFjZSA9IFRSVUUpKSRwYXIKaGVhZChCb290c3RyYXBNb2RlbHMpCgojIHN0YW5kYXJkIGVycm9yIApzZCh+IFYxLCBkYXRhID0gQm9vdHN0cmFwTW9kZWxzKSAjIGludGVyY2VwdApzZCh+IFYyLCBkYXRhID0gQm9vdHN0cmFwTW9kZWxzKSAjIHNsb3BlCgojIDk1JSBDSSAocGVyY2VudGlsZSBtZXRob2QpCnFkYXRhKH4gVjEsIHAgPSBjKDAuMDI1LCAwLjk3NSksIGRhdGEgPSBCb290c3RyYXBNb2RlbHMpICMgaW50ZXJjZXB0CnFkYXRhKH4gVjIsIHAgPSBjKDAuMDI1LCAwLjk3NSksIGRhdGEgPSBCb290c3RyYXBNb2RlbHMpICMgc2xvcGUKCmBgYAoKIyMjIyBDb21wYXJlIGBsbSgpYCByZXN1bHQgdG8gQm9vdHN0cmFwIAoKLSBOb3RlIFN0YW5kYXJkIEVycm9yIGVzdGltYXRlcyBmb3IgZWFjaCBjb2VmZmljaWVudAotIGNvbXBhcmUgY29uZmlkZW5jZSBpbnRlcnZhbCBlc3RpbWF0ZXMKLSBROiB3aGF0IGlmIHdlIHJ1biB0aGUgc2ltdWxhdGlvbiBhZ2Fpbj8KLSBROiB3aGF0IGlmIHdlIHJ1biAqKm1vcmUqKiBib290c3RyYXAgbW9kZWxzPwoKYGBge3J9CiMgcmVjYWxsIG91ciBgbG0oKWAgbW9kZWwgZm9yIGNvbXBhcmlzb24KbXN1bW1hcnkoc2ltMV9sbSkKY29uZmludChzaW0xX2xtKQoKYGBgCgoKIyMgRXhlcmNpc2U6IE1lYW4gQWJzb2x1dGUgRGV2aWF0aW9uCgotIE9uZSB3ZWFrbmVzcyB3ZSBhcmUgdG9sZXJhdGluZyBpcyBhIHNlbnNpdGl2aXR5IHRvIGV4dHJlbWUgb2JzZXJ2YXRpb25zCi0gQ29uc2lkZXIgTWVhbiBBYnNvbHV0ZSBEZXZpYXRpb24gdnMuIFJvb3QtTWVhbi1TcXVhcmUgRGV2aWF0aW9uCiAgICAtIFE6IENvbXBhcmUgJiBjb250cmFzdCB0aGUgdHdvIGFwcHJvYWNoZXMKICAgIC0gUTogSG93IGNvdWxkIHlvdSBmaXQgYSBtb2RlbCB0aGF0IG1pbmltaXplcyB0aGUgTUFEIG1ldHJpYz8KICAgICAgICAtIHVzZSBgc2ltMWAgZGF0YSBmcm9tIGBtb2RlbHJgIHBhY2thZ2U/Cgo8IS0tIERheTUgLS0+CgojIyBFdmFsdWF0aW5nIG1vZGVsIGZpdAoKLSBPdXIgbW9kZWwgZXNzZW50aWFsbHkgcGFydGl0aW9ucyB0aGUgaW5mb3JtYXRpb24gKGkuZS4gdmFyaWFiaWxpdHkpIGluIHRoZSBkYXRhIGludG8gdHdvIHBhcnRzCiAgICAxLiBzdHJ1Y3R1cmUgdGhhdCBJUyBleHBsYWluZWQgYnkgdGhlIG1vZGVsIChwcmVkaWN0aW9ucykKICAgIDIuIHJhbmRvbW5lc3MgdGhhdCBJUyBOT1QgZXhwbGFpbmVkIGJ5IHRoZSBtb2RlbCAocmVzaWR1YWxzIG9yICJlcnJvcnMiKQotIE5vdGU6IFRoZSBwb3B1bGFyIFItc3F1YXJlZCBzdGF0aXN0aWMgaXMgYSBzaW1wbGUgZGVzY3JpcHRpb24gb2YgdGhpcyBwYXJ0aXRpb24KCgojIyBNb2RlbCBwcmVkaWN0aW9ucwoKLSBSZWdyZXNzaW9uIGNvdWxkIGJlIGRlc2NyaWJlZCBhcyAiY29uZGl0aW9uYWwgZXhwZWN0YXRpb24iCi0gTGV0J3MgY29uc2lkZXIgc2tldGNoaW5nIG91dCBvdXIgZXhwZWN0YXRpb25zLCB1bmRlciB2YXJpb3VzIGNvbmRpdGlvbnMuLi4KICAgIC0gV2Ugd2FudCB0byB1c2Ugb3VyIG1vZGVsIHRvIGNhbGN1bGF0ZWQgdGhlIGV4cGVjdGVkIHZhbHVlIG9mIFkgYXQgYSBidW5jaCBvZiBkaWZmZXJlbnQgdmFsdWVzIG9mIFgKICAgIC0gV2UgbmVlZCBhIHNlcXVlbmNlIG9mIFggdmFsdWVzIGZvciBvdXIgaW52ZXN0aWdhdGlvbiB1c2luZyBgbW9kZWxyOjpkYXRhX2dyaWQoKWAKICAgIC0gV2UnbGwgdGhlbiBjYWxjdWxhdGUgdGhlIGV4cGVjdGVkIHZhbHVlIG9mIFkgKGkuZS4gcHJlZGljdGlvbikgYXQgZWFjaCBvbmUgdXNpbmcgYG1vZGVscjo6YWRkX3ByZWRpY3Rpb25zKClgCgoKIyMjIyBJbml0aWFsaXplIHRoZSBncmlkCgpgYGB7cn0KZ3JpZCA8LSAKICBzaW0xICU+JQogIGRhdGFfZ3JpZCh4KQoKIyBqdXN0IGEgYnVuY2ggb2YgZXZlbmx5IHNwYWNlZCBoeXBvdGhldGljYWwgdmFsdWVzIGZvciBYLi4uCmhlYWQoZ3JpZCkKCmBgYAoKIyMjIyBBZGQgc29tZSBwcmVkaWN0aW9ucyB1c2luZyBvdXIgZXhpc3RpbmcgbW9kZWwgKGBzaW0xX2xtYCkKCmBgYHtyfQpncmlkIDwtIAogIGdyaWQgJT4lCiAgYWRkX3ByZWRpY3Rpb25zKHNpbTFfbG0pCgojIHByZWRpY3Rpb25zIGFjY29tcGFueWluZyBlYWNoIGh5cG90aGV0aWNhbCBYIHZhbHVlIGluIG91ciBncmlkCmhlYWQoZ3JpZCkKYGBgCgojIyMjIFBsb3QgaXQhCgotIEFkbWl0dGVkbHksIHRoaXMgaXNuJ3QgdGhlIG1vc3QgZGlyZWN0IHdheSB0byBhZGQgYSByZWdyZXNzaW9uIGxpbmUgdG8gYSBwbG90IChlLmcuLCBgZ2VvbV9hYmxpbmUoKWApCi0gKipUaGUgYmVuZWZpdCoqOiB0aGlzIHNpbXBsZSBhcHByb2FjaCBleHRlbmRzIHRvIGp1c3QgYWJvdXQgKipBTlkqKiBtb2RlbCBpbiBSIChldmVuIGNvbXBsZXggbW9kZWxzKQotIFlvdXIgcHJpbWFyeSBsaW1pdGF0aW9uIG5vdyBpcyB2aXN1YWxpemF0aW9uIHNraWxscyAoTk9UIG1vZGVsIGNvbXBsZXhpdHkpCgpgYGB7cn0KIyBqdXN0IHRoZSBkYXRhCmdncGxvdChzaW0xLCBhZXMoeCkpICsKICBnZW9tX3BvaW50KGFlcyh5ID0geSkpCgojIGFkZCB0aGUgZ3JpZCBvZiBwcmVkaWN0aW9ucyB0byB0aGUgZGF0YQpnZ3Bsb3Qoc2ltMSwgYWVzKHgpKSArCiAgZ2VvbV9wb2ludChhZXMoeSA9IHkpKSArCiAgZ2VvbV9wb2ludChhZXMoeSA9IHByZWQpLCBkYXRhID0gZ3JpZCwgY29sb3VyID0gInJlZCIsIHNpemUgPSAzKQoKIyBzaW5jZSB0aGUgbW9kZWwgZmFtaWx5IGlzIGxpbmVhci4uLiAKZ2dwbG90KHNpbTEsIGFlcyh4KSkgKwogIGdlb21fcG9pbnQoYWVzKHkgPSB5KSkgKwogIGdlb21fbGluZShhZXMoeSA9IHByZWQpLCBkYXRhID0gZ3JpZCwgY29sb3VyID0gInJlZCIsIHNpemUgPSAxKQpgYGAKCiMjIFJlc2lkdWFscwoKLSAqKnByZWRpY3Rpb25zKiogaWxsdXN0cmF0ZSB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIGRhdGEgKmNhcHR1cmVkKiBieSB0aGUgbW9kZWwKLSAqKnJlc2lkdWFscyoqIHRlbGwgeW91IHdoYXQgdGhlIG1vZGVsIGNvdWxkbid0IGNhcHR1cmUvZXhwbGFpbgoKIVtdKGFic0Vycm9yc1I0RFMucG5nKQoKCgojIyBSZXNpZHVhbHMKCi0gKipwcmVkaWN0aW9ucyoqIGlsbHVzdHJhdGUgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSBkYXRhICpjYXB0dXJlZCogYnkgdGhlIG1vZGVsCi0gKipyZXNpZHVhbHMqKiB0ZWxsIHlvdSB3aGF0IHRoZSBtb2RlbCBjb3VsZG4ndCBjYXB0dXJlL2V4cGxhaW4KLSBROiB3aGF0IGRvIHdlIGxlYXJuIGZyb20gdGhpcyBwbG90PwoKYGBge3J9CnNpbTEgPC0gCiAgc2ltMSAlPiUKICBhZGRfcHJlZGljdGlvbnMoc2ltMV9sbSkgJT4lCiAgYWRkX3Jlc2lkdWFscyhzaW0xX2xtKQoKIyB3ZSd2ZSBqdXN0IGFwcGVuZGVkIHRoZSBtb2RlbCByZXNpZHVhbHMgdG8gb3VyIGRhdGEgc2V0CmhlYWQoc2ltMSkKCiMgTGV0J3MgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHJlc2lkdWFscwpzaW0xICU+JQogIGdncGxvdChhZXMocmVzaWQpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC41KQoKIyBvciBjb21tb25seSB3aXRoIGEgZGVuc2l0eSBwbG90CnNpbTEgJT4lCiAgZ2dwbG90KGFlcyhyZXNpZCkpICsgCiAgZ2VvbV9kZW5zaXR5KCkKYGBgCgojIyMjIFJlc2lkdWFscyB2cyBYIChvdXIgcHJlZGljdG9yIHZhcmlhYmxlKQoKLSBROiBXaGF0IGRvIHdlIGxlYXJuIGhlcmU/CgpgYGB7cn0Kc2ltMSAlPiUKICBnZ3Bsb3QoYWVzKHgsIHJlc2lkKSkgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9IDIpICsgCiAgZ2VvbV9wb2ludCgpCmBgYAoKCjwhLS0gUGljayB1cCBmcm9tIFI0RFMgMjMuNC4xOiBNb2RlbCBGYW1pbGllcyAgLS0+CgojIyBGb3JtdWxhIHN5bnRheCAKCi0gTW9kZWxzIHJlcXVpcmUgdGhhdCB3ZSB1c2UgYSBmb3JtdWxhIHN5bnRheCBpbiBSLCBmb3IgZXhhbXBsZSBgeSB+IHgxICsgeDJgCiAgICAtIE5vdGU6IGAgfiB5YCBpcyBzb21ldGltZXMgc2hvcnRoYW5kIGZvciBgeSB+IDFgIChlLmcuLCBgbW9zYWljYCBwYWNrYWdlKQogICAgLSBgeWAgaXMgdGhlIG91dGNvbWUgKHJlc3BvbnNlKSB2YXJpYWJsZSBvZiBpbnRlcmVzdAogICAgLSBgeDFgIGFuZCBgeDJgIGFyZSAqKmV4cGxhbmF0b3J5KiogdmFyaWFibGVzLi4uIHBvc3NpYmx5IGNvbnRpbnVvdXMgT1IgY2F0ZWdvcmljYWwKLSBGb3JtdWxhIHN5bnRheCBpcyBmYWlybHkgc2VsZi1ldmlkZW50IHdoZW4gYWxsIHZhcmlhYmxlcyBhcmUgcXVhbnRpdGF0aXZlCiAgICAtIHByZXR0eSBzaW1pbGFyIHRvIEhTIGFsZ2VicmEKICAgIC0gYHkgfiB4YCBtZWFucyAkeSA9IGEgKyBiKngkLi4uIGEgbGluZSBpbiB0d28gZGltZW5zaW9ucwogICAgLSBgeSB+IHgxICsgeDJgIG1lYW5zICR5ID0gYSArIGIqeF8xICsgYyp4XzIkLi4uIGEgbGluZSBpbiB0aHJlZSBkaW1lbnNpb25zCiAgICAtIGB5IH4geCArIEkoeF4yKWAgbWVhbnMgJHkgPSBhICsgYip4ICsgYyp4XjIkLi4uIGEgcXVhZHJhdGljCi0gVGhpbmdzIGFyZSBsZXNzIG9idmlvdXMgd2hlbiBhICoqY2F0ZWdvcmljYWwqKiB2YXJpYWJsZSBpcyBpbnZvbHZlZAogICAgLSBgeSB+IHNleGAgd2hlcmUgc2V4IGlzIGVpdGhlciAibWFsZSIgb3IgImZlbWFsZSIuLi4KICAgIC0gJHkgPSBhICsgYiooc2V4KSQgZG9lc24ndCBtYWtlIHNlbnNlLi4uIHNleCBpc24ndCBhIG51bWJlciB0aGF0IGNhbiBiZSBtdWx0aXBsaWVkCiAgICAtIFIgY29udmVydHMgYHNleGAgdG8gYW4gKippbmRpY2F0b3IqKiB2YXJpYWJsZTogYHNleG1hbGVgIChvciBgc2V4ZmVtYWxlYCkKICAgIC0gYHNleG1hbGVgIGlzIDEgaWYgYHNleGAgaXMgbWFsZSwgYW5kIDAgaWYgYHNleGAgaXMgZmVtYWxlCiAgICAtIGB5IH4gc2V4YCBiZWNvbWVzICR5ID0gYSArIGIqc2V4bWFsZSQgYW5kIHdlJ3JlIGJhY2sgaW4gYnVzaW5lc3MKLSBROiB3aHkgZG9lc24ndCBSIGNyZWF0ZSBCT1RIIGBzZXhtYWxlYCBhbmQgYHNleGZlbWFsZWA/Ci0gUTogd2h5IGFyZSB0aGVzZSBhbGwgc3RpbGwgY2FsbGVkICJsaW5lYXIiIG1vZGVscz8KCiMjIE1vZGVsaW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcwoKLSBjb25zaWRlciBhIG5ldyB0b3kgZGF0YSBzZXQgY2FsbGVkIGBzaW0yYAogICAgLSBpbnNwZWN0IHN0cnVjdHVyZSAmIHZhcmlhYmxlcwogICAgLSBwbG90IHRoZSBkYXRhCiAgICAtIGZpdCBtb2RlbCB3aXRoIGBsbSgpYAogICAgLSBnZW5lcmF0ZSBwcmVkaWN0aW9ucwotIFE6IHdoeSBhcmUgdGhlcmUgNCBwcmVkaWN0aW9ucywgYW5kIG5vdCA0MD8KCmBgYHtyfQojIGluc3BlY3QgZGF0YSBzdHJ1Y3R1cmVzCnN0cihzaW0yKQoKIyBzaG93IHNjYXR0ZXIgcGxvdCBvZiBjYXRlZ29yaWNhbCBgeGAgdmFyaWFibGUKc2ltMiAlPiUKICBnZ3Bsb3QoKSArIAogIGdlb21fcG9pbnQoYWVzKHgsIHkpKQoKIyBmaXQgYSBzaW1wbGUgbW9kZWwgYW5kIGdlbmVyYXRlIHByZWRpY3Rpb25zIGFnYWluCm1vZDIgPC0gbG0oeSB+IHgsIGRhdGEgPSBzaW0yKQoKZ3JpZCA8LSAKICBzaW0yICU+JSAKICBkYXRhX2dyaWQoeCkgJT4lIAogIGFkZF9wcmVkaWN0aW9ucyhtb2QyKQpncmlkCmBgYAoKCiMjIE1vZGVsaW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcwoKLSBROiBXaHkgZG9lcyBhIG1vZGVsIHdpdGggY2F0ZWdvcmljYWwgYHhgIHdpbGwganVzdCBwcmVkaWN0IHRoZSBtZWFuIHZhbHVlIGZvciBlYWNoIGNhdGVnb3J5PwotIFE6IFdoYXQgaXMgb3VyIGRpc3RhbmNlIGNyaXRlcmlvbiBmb3IgZml0dGluZyB0aGUgImJlc3QiIG1vZGVsPwoKCmBgYHtyfQpzaW0yICU+JQogIGdncGxvdChhZXMoeCkpICsgCiAgZ2VvbV9wb2ludChhZXMoeSA9IHkpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZ3JpZCwgYWVzKHkgPSBwcmVkKSwgY29sb3VyID0gInJlZCIsIHNpemUgPSA0KQpgYGAKCgojIyBNb2RlbGluZyB3aXRoIGludGVyYWN0aW9ucyAKCi0gc29tZXRpbWVzIHdlIG5lZWQgdG8gY29tYmluZSBpbmZvcm1hdGlvbiBhYm91dCBhIHF1YW50aXRhdGl2ZSAmIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgaW4gdGhlIHNhbWUgbW9kZWwgCi0gSW50ZXJwcmV0YXRpb246IHdlIGNhbid0IGV4cGxhaW4gdGhlIGltcGFjdCBvZiAqKm9uZSoqIHZhcmlhYmxlICh4MSkgd2l0aG91dCBjb25zaWRlcmluZyAqKmFub3RoZXIqKiB2YXJpYWJsZSAoeDIpCi0gdG95IGV4YW1wbGUKICAgIC0gZGF0YTogYHNpbTNgCiAgICAtIG5vdGU6IEknbSB1c2luZyBzbGlnaHRseSBpcnJlZ3VsYXIgc3ludGF4IHRvIGNsYXJpZnkgdGhlIHBvaW50CgpgYGB7cn0KIyBpbnNwZWN0IGRhdGEgc3RydWN0dXJlcwpzdHIoc2ltMykKCiMgc2hvdyBzY2F0dGVyIHBsb3Qgb2YgY2F0ZWdvcmljYWwgYHhgIHZhcmlhYmxlCnNpbTMgJT4lCiAgZ2dwbG90KCkgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0geDEsIHkgPSB5LCBjb2xvciA9IHgyKSkKCiMgZml0IGEgc2ltcGxlIG1vZGVsIGFuZCBnZW5lcmF0ZSBwcmVkaWN0aW9ucyBhZ2Fpbgptb2QzXzEgPC0gbG0oeSB+IHgxICsgeDIsIGRhdGEgPSBzaW0zKQptb2QzXzIgPC0gbG0oeSB+IHgxICsgeDIgKyB4MSp4MiwgZGF0YSA9IHNpbTMpCgojIGdlbmVyYXRlIG1vZGVsIHByZWRpY3Rpb25zCmdyaWQgPC0gCiAgc2ltMyAlPiUgCiAgZGF0YV9ncmlkKHgxLCB4MikgJT4lIAogIGFkZF9wcmVkaWN0aW9ucyhtb2QzXzEsIHZhciA9ICJtb2QxIikgJT4lCiAgYWRkX3ByZWRpY3Rpb25zKG1vZDNfMiwgdmFyID0gIm1vZDIiKQpncmlkCgpgYGAKCiMjIyMgUGxvdCB0aGUgaW50ZXJhY3Rpb24gbW9kZWwKCi0gd2Ugc2hvdWxkIHN0YWNrIChpLmUuIGdhdGhlcikgdGhlIHByZWRpY3Rpb25zIHNvIHdlIGNhbiBtYWtlIHRoZSBtb2RlbCBjb21wYXJpc29ucyBvbiB0aGUgc2FtZSBwbG90Ci0gUTogV2hhdCdzIGhhcHBlbmluZyBoZXJlPyByZWNhbGwuLi4KICAgIC0gYG1vZDNfMSA8LSBsbSh5IH4geDEgKyB4MiwgZGF0YSA9IHNpbTMpYAogICAgLSBgbW9kM18yIDwtIGxtKHkgfiB4MSArIHgyICsgeDEqeDIsIGRhdGEgPSBzaW0zKWAKICAgIC0gaW50ZXJwcmV0YXRpb24gb2YgKippbnRlcmFjdGlvbioqCgoKYGBge3J9CmdyaWRTdGFjayA8LSAKICBncmlkICU+JQogIGdhdGhlcihrZXkgPSAibW9kZWwiLCB2YWx1ZSA9ICJwcmVkIiwgbW9kMSwgbW9kMikKCiMgbW9kZWwgcGxvdApzaW0zICU+JQogIGdncGxvdChhZXMoeCA9IHgxLCB5ID0geSwgY29sb3IgPSB4MikpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9saW5lKGRhdGEgPSBncmlkU3RhY2ssIGFlcyh5ID0gcHJlZCkpICsgCiAgZmFjZXRfd3JhcCh+IG1vZGVsKQoKIyBzYW1lIG1vZGVsLCBidXQgZmFjZXRzIHNlcGFyYXRlIGVhY2ggbW9kZWwgJiB4MiBjb21iaW5hdGlvbgpzaW0zICU+JQogIGdncGxvdChhZXMoeCA9IHgxLCB5ID0geSwgY29sb3IgPSB4MikpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9saW5lKGRhdGEgPSBncmlkU3RhY2ssIGFlcyh5ID0gcHJlZCkpICsgCiAgZmFjZXRfZ3JpZChtb2RlbCB+IHgyKQpgYGAKCiMjIyMgV3JpdGUgZG93biBlYWNoIG1vZGVsCgotIG1hbnkgcGVvcGxlIHVuZGVyc3RhbmQgdGhlIGludGVyYWN0aW9uIGJldHRlciBieSB3cml0aW5nIGRvd24gdGhlIG1vZGVscwotIGhlcmUncyB0aGUgbW9kZWwgb3V0cHV0IHNvIHdlIGNhbiB3b3JrIHRoZW0gb3V0CgpgYGB7cn0Kc3VtbWFyeShtb2QzXzEpCnN1bW1hcnkobW9kM18yKQpgYGAKCjwhLS0gRGF5NiAtLT4KCiMjIFBsb3QgdGhlIG1vZGVsIChgbW9zYWljOjpwbG90TW9kZWwoKWApCgotIG5vdGU6IGZvciBzaW1wbGVyIG1vZGVsIGZvcm1zLCB0aGVyZSBhcmUgZ29vZCBmdW5jdGlvbnMgdG8gaGVscCAoZS5nLiwgYG1vc2FpY2AgcGFja2FnZSkKLSBzYW1lIG1vZGVscyBhcyBiZWZvcmU6CiAgICAtIGBtb2QzXzEgPC0gbG0oeSB+IHgxICsgeDIsIGRhdGEgPSBzaW0zKWAKICAgIC0gYG1vZDNfMiA8LSBsbSh5IH4geDEgKyB4MiArIHgxKngyLCBkYXRhID0gc2ltMylgCi0gYG1wbG90KClgIGlzIGEgcHJldHR5IGhhbmR5IGZ1bmN0aW9uIAogICAgLSAqKmNvbnNvbGUqKjogYG1wbG90KHNpbTEpYCBvcGVucyBpbnRlcmFjdGl2ZSBncmFwaC1idWlsZGVyIHRvIGdldCBzdGFydGVkIHdpdGggRURBCiAgICAtICoqcmVncmVzc2lvbioqOiBgbXBsb3QobW9kZWxGaXQpYCBwcm9kdWNlcyBhIGZldyBjb21tb24gcmVzaWR1YWwgcGxvdHMgCiAgICAgICAgLSBjaG9vc2UgYSBzaW5nbGUgcGxvdCB3aXRoIGBtcGxvdChtb2RlbEZpdCwgd2hpY2ggPSAxKWAgCiAgICAgICAgLSBmb3VyLXBhY2sgb2YgY29tbW9uIHBsb3RzIHdpdGggYG1wbG90KG1vZGVsRml0KWAKICAgICAgICAtIG90aGVyIG9wdGlvbnMgYXZhaWxhYmxlCgpgYGB7cn0KIyByZXByb2R1Y2Ugb3VyIG1vZGVsIHBsb3RzCnBsb3RNb2RlbChtb2QzXzEpICAjIG5vIGludGVyYWN0aW9uIChwYXJhbGxlbCBsaW5lcykKcGxvdE1vZGVsKG1vZDNfMikgICMgaW50ZXJhY3Rpb24gICAgKG5vbi1wYXJhbGxlbC4uLnNsb3BlIGNhbiB2YXJ5IGJ5IGdyb3VwKQoKIyByZXNpZHVhbHMgdnMgZml0dGVkIHZhbHVlcwptcGxvdChtb2QzXzEsIHdoaWNoID0gMSkKbXBsb3QobW9kM18yLCB3aGljaCA9IDEpCgojIHdoYXQncyB0aGlzPwptcGxvdChtb2QzXzIsIHdoaWNoID0gNykKYGBgCgoKCiMjIEludGVyYWN0aW9ucyAoY29udGludW91czpjb250aW51b3VzKQoKLSBSZWNhbGw6IENvbnRpbnVvdXM6Q2F0ZWdvcmljYWwgaW50ZXJhY3Rpb24gYWxsb3dzIGRpZmZlcmVudCBzbG9wZSAmIGludGVyY2VwdCBkZXBlbmRpbmcgb24gdGhlIHZhbHVlIG9mIGNhdGVnb3JpY2FsIHZhcmlhYmxlCi0gU2ltaWxhcmx5OiBDb250aW51b3VzOkNvbnRpbnVvdXMgaW50ZXJhY3Rpb24gYWxsb3dzIGRpZmZlcmVudCBzbG9wZSAmIGludGVyY2VwdCBkZXBlbmRpbmcgb24gdGhlIHZhbHVlIG9mIHRoZSBvdGhlciBjb250aW51b3VzIHZhcmlhYmxlLgogICAgLSBUaGluayBvZiBhIHR3aXN0ZWQgcGllY2Ugb2YgZ3JhcGggcGFwZXIuLi4gZXZlcnkgY29tYmluYXRpb24gb2YgWDE6WDIgaXMgc3RpbGwgbGluZWFyLCBidXQgdGhlIHNsb3BlcyBhbmQgaW50ZXJjZXB0cyB2YXJ5CiAgICAtIFNvbWV0aW1lcyBwbG90dGVkIGFzIDNEIHdpcmUgZGlhZ3JhbXMgKGxpa2UgdGhlIGdyYXBoIHBhcGVyIGlkZWEpCiAgICAtIE9mdGVuIHBsb3R0ZWQgYXMgMkQgaGVhdCBtYXAgb3IgY29udG91ciBwbG90CgohW2ltYWdlIGNyZWRpdDogaHR0cHM6Ly9ibG9ncy51b3JlZ29uLmVkdS9yY2x1Yi8yMDE2LzEwLzA1L2xvb2tpbmctYXQtaW50ZXJhY3Rpb25zLW9mLWNvbnRpbnVvdXMtdmFyaWFibGVzL10oaW50ZXJhY3Rpb24ucG5nKQoKCgojIyBPcmlkaW5hcnkgTGVhc3QgU3F1YXJlcyAoT0xTKSBSZWdyZXNzaW9uIFN1bW1hcnkKCi0gTGluZWFyIHJlbGF0aW9uc2hpcHMgYXJlIHZlcnkgY29udmVuaWVudCBmb3IgaW50ZXJwcmV0IGFuZCB1bmRlcnN0YW5kCi0gTXVsdGlwbGUgcmVncmVzc2lvbiBpcyBhIGdyZWF0IHRvb2wgZm9yIGF0dGFja2luZyBsb3RzIG9mIGludGVyZXN0aW5nIHF1ZXN0aW9ucwotIFRoZSBhc3N1bXB0aW9ucyBhcmUgcHJldHR5IHN0cmFpZ2h0LWZvcndhcmQgKEwtSS1OLUUpCiAgICAtICoqTCoqaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIG1lYW4gcmVzcG9uc2UgKFkpIGFuZCBlYWNoIGV4cGxhbmF0b3J5IHZhcmlhYmxlIChYKQogICAgLSBvYnNlcnZhdGlvbnMgYXJlICoqSSoqbmRlcGVuZGVudAogICAgLSByZXNwb25zZXMgYXJlICoqTioqb3JtYWxseSBkaXN0cmlidXRlZCBhdCBlYWNoIGxldmVsIG9mIFgKICAgIC0gdmFyaWFuY2Ugb2YgdGhlIHJlc3BvbnNlIGlzICoqRSoqcXVhbCBmb3IgYWxsIGxldmVscyBvZiBYCgohW10oT0xTLW1vZGVsLnBuZykKCiMjIE9MUyBSZWdyZXNzaW9uIEZhaWwKCioqUHJvYmxlbTogbG90cyBvZiBpbnRlcmVzdGluZyBxdWVzdGlvbnMgY2FuJ3QgYmUgYW5zd2VyZWQgYnkgZGF0YSB0aGF0IHNhdGlzZnkgdGhvc2UgY29uZGl0aW9ucy4qKgoKQ29uc2lkZXIgYSBjb3VwbGUgb2YgZXhhbXBsZXM6IAoKLSAqKkdyYWRlcyAmIFN0dWR5aW5nKioKLSBJcyB0aGUgdGltZSBzcGVudCBzdHVkeWluZyBwcmVkaWN0aXZlIG9mIHN1Y2Nlc3Mgb24gYW4gZXhhbT8gIFRoZSB0aW1lIHNwZW50IHN0dWR5aW5nIGZvciBhbiBleGFtIChpbiBob3VycykgYW5kIHJlc3VsdCAocGFzcy9mYWlsKSBhcmUgcmVjb3JkZWQgZm9yIHJhbmRvbWx5IHNlbGVjdGVkIHN0dWRlbnRzLgoKPi0gc2luY2UgdGhlIG9iamVjdGl2ZSBpcyB1bHRpbWF0ZWx5IHRvIGFzc29jaWF0ZSB0aGUgcHJvYmFiaWxpdHkgb2Ygc3VjY2VzcywgJHAkLCB3aXRoIHRoZSBjb3ZhcmlhdGVzLCBvbmUgbWlnaHQgYmUgdGVtcHRlZCB0byB0cnkgYSBsaW5lYXIgbW9kZWwgZm9yIHRoZSBhdmVyYWdlIGJpbmFyeSByZXNwb25zZSwgJHAkOiAkcF97aX0gPSBcYmV0YV97MH0gKyBcYmV0YV97MX14X3tpfSQKPi0gUTogV2hhdCdzIHRoZSAqKk9MUyB2aW9sYXRpb24qKj8gCj4tIGUuZy4sIHRoZSByZXNwb25zZSB2YXJpYWJsZSBpcyBiaW5hcnksIHdoaWNoIHZpb2xhdGVzIHRoZSBPTFMgYXNzdW1wdGlvbiBvZiBhIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHJlc3BvbnNlIGF0IGVhY2ggbGV2ZWwgb2YgWAo+LSBSZXN1bHQ6ICRwX3tpfSA9IFxiZXRhX3swfSArIFxiZXRhX3sxfXhfe2l9JCBkb2Vzbid0IHdvcmsgd2VsbCBmb3IgYmVybm91bGxpL2Jpbm9taWFsIHJlc3BvbnNlCj4tICFbQmlub21pYWwgUmVncmVzc2lvbl0oYmlub21pYWwtbW9kZWwucG5nKQoKCiMjIE9MUyBSZWdyZXNzaW9uIEZhaWwKCioqUHJvYmxlbTogbG90cyBvZiBpbnRlcmVzdGluZyBxdWVzdGlvbnMgY2FuJ3QgYmUgYW5zd2VyZWQgYnkgZGF0YSB0aGF0IHNhdGlzZnkgdGhvc2UgY29uZGl0aW9ucy4qKgoKQ29uc2lkZXIgYSBjb3VwbGUgb2YgZXhhbXBsZXM6IAoKLSAqKkVSIFZpc2l0cyAmIFBvbGx1dGlvbioqOiAKLSBJcyB0aGUgbnVtYmVyIG9mIGFzdGhtYS1yZWxhdGVkIHZpc2l0cyB0byBhIGhvc3BpdGFsIEVtZXJnZW5jeSBSb29tIGFzc29jaWF0ZWQgd2l0aCB0aGUgYWlyIHF1YWxpdHkgaW5kZXggZm9yIHRoZSBkYXk/ICBUaGUgdG90YWwgbnVtYmVyIG9mIEVSIHBhdGllbnRzIChjb3VudCkgYW5kIHRoZSBhaXIgcXVhbGl0eSBpbmRleCAoY29udGludW91cyBtZWFzdXJlbWVudCkKCj4tIG9uZSBtaWdodCBiZSB0ZW1wdGVkIHRvIHRyeSBhbmQgbW9kZWwgJFxsYW1iZGFfe2l9JCBhcyBhIGxpbmVhciBmdW5jdGlvbiBvZiB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGU6ICRcbGFtYmRhX3tpfSA9IFxiZXRhX3swfSArIFxiZXRhX3sxfXhfe2l9JAo+LSBROiBXaGF0J3MgdGhlICoqT0xTIHZpb2xhdGlvbioqPwo+LSBlLmcuLCB0aGUgcmVzcG9uc2UgdmFyaWFibGUgaXMgUG9pc3NvbiBzbyB3ZSBleHBlY3QgdGhhdCBtZWFuID0gdmFyaWFuY2UgPSAkXGxhbWJkYSQsIHdoaWNoIHZpb2xhdGVzIHRoZSBPTFMgYXNzdW1wdGlvbiBvZiBlcXVhbCB2YXJpYW5jZSBmb3IgYWxsIGxldmVscyBvZiBYCj4tIFJlc3VsdDogJFxsYW1iZGFfe2l9ID0gXGJldGFfezB9ICsgXGJldGFfezF9eF97aX0kIGRvZXNuJ3Qgd29yayB3ZWxsIGZvciBQb2lzc29uIHJlc3BvbnNlCj4tICFbUG9pc3NvbiBSZWdyZXNzaW9uXShwb2lzc29uLW1vZGVsLnBuZykKCgojIyBPTFMgUmVncmVzc2lvbiBGYWlsCgp8IFJlc2VhcmNoZXJzIHwgSW50ZXJlc3RzICB8IFZhcmlhYmxlIFR5cGUgfCAKfDotLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLXwKfCBFY29sb2dpc3RzOiB8IE51bWJlciBvZiBTcGVjaWVzIHwgUG9pc3NvbiB8CnwgQ3JpbWlub2xvZ2lzdHM6IHwgQXJyZXN0IENvdW50IHwgUG9pc3NvbiB8CnwgQ2FuY2VyIHNwZWNpYWxpc3Q6IHwgTnVtYmVyIG9mIGNhc2VzIHwgUG9pc3NvbiB8CnwgUG9saXRpY2FsIFNjaWVudGlzdHM6IHwgV2hv4oCZcyBhIGRlbW9jcmF0ZT8gfCBCZXJub3VsbGkgb3IgQmlub21pYWwgfCAKfCBQcmUtbWVkIFN0dWRlbnQ6IHwgV2hvIGdldHMgaW50byBtZWQgc2Nob29sPyAgfCBCZXJub3VsbGkgb3IgQmlub21pYWwgfAp8IFNvY2lvbG9naXN0OiB8IFdob+KAmXMgZ290IGEgdGF0dG9vPyB8IEJlcm5vdWxsaSBvciBCaW5vbWlhbCB8IAoKCiMjIEdlbmVyYWxpemVkIExpbmVhciBNb2RlbHMgKEdMTXMpCgojIyMgT25lLXBhcmFtZXRlciBleHBvbmVudGlhbCBmYW1pbHkgCgotIEluIHRoZSBlYXJseSAxOTcwcyBOZWxkZXIgJiBXZWRkZXJidXJuIGlkZW50aWZpZWQgYSBicm9hZGVyIGNsYXNzIG9mIG1vZGVscyB0aGF0IGdlbmVyYWxpemVzIHRoZSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBhcHByb2FjaCB1bmRlciBjZXJ0YWluIGNvbmRpdGlvbnM6IAogICAgMS4gVGhlIHByb2JhYmlsaXR5IGZvcm11bGEgKHBkZiBvciBwbWYpIGNhbiBiZSB3cml0dGVuIGFzIGZvbGxvd3M6CiAgICAKICAgIFxbZih5OyBcdGhldGEpPWV4cF57KGEoeSliKFx0aGV0YSkgKyBjKFx0aGV0YSkgKyBkKHkpKX1cXQogICAgCiAgICAyLiB0aGUgc2V0IG9mIHBvc3NpYmxlIHZhbHVlcyAoaS5lLiB0aGUgc3VwcG9ydCkgZG9lcyBub3QgZGVwZW5kIHVwb24gYSBwYXJhbWV0ZXIKCi0gSWYgc28sIGl0IGlzIHNhaWQgdG8gaGF2ZSAqKm9uZS1wYXJhbWV0ZXIgZXhwb25lbnRpYWwgZmFtaWx5IGZvcm0qKi4uLiAKCjxicj4KCiMjIyAuLi5IZXJlJ3MgdGhlIGNvb2wgcGFydCAKCj4tICRcbXUgPSAtXGZyYWN7YycoXHRoZXRhKX17YicoXHRoZXRhKX0kCgo+LSAkXHNpZ21hXjIgPSBcZnJhY3tiJycoXHRoZXRhKWMnKFx0aGV0YSkgLSBjJycoXHRoZXRhKWInKFx0aGV0YSl9e1tiJyhcdGhldGEpXV4zfSQKCj4tIChkcnVtIHJvbGwuLi4pCgo+LSAkYihcdGhldGEpID0gXGJldGFfMCArIFxiZXRhXzFYICsgLi4uJCBpcyBhICoqbGluZWFyKiogbW9kZWwhCgo+LSAkYihcdGhldGEpJCBpcyBjYWxsZWQgdGhlICoqY2Fub25pY2FsIGxpbmsqKiBmdW5jdGlvbiwgbWVhbmluZyBpdCdzIG9mdGVuIGEgZ29vZCBjaG9pY2UgdG8gbW9kZWwgYXMgYSAqKmxpbmVhcioqIGZ1bmN0aW9uIG9mIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMgKGJ1dCBvdGhlciBsaW5rIGZ1bmN0aW9ucyBleGlzdCBhbmQgbWlnaHQgYmUgcHJlZmVycmVkIHVuZGVyIHNwZWNpYWwgY2lyY3Vtc3RhbmNlcykuCgojIyBHTE0gZ3V0LWNoZWNrIChQb2lzc29uKQoKIyMjIEdvYWw6CgpcW2YoeTsgXHRoZXRhKT1leHBeeyhhKHkpYihcdGhldGEpICsgYyhcdGhldGEpICsgZCh5KSl9XF0KCioqV2hhdCBpZiBvdXIgcmVzcG9uc2UgdmFyaWFibGUgaXMuLi4qKgoKIyMjIFBvaXNzb24/CgotIHN1cHBvcnQ6IHNpbmNlIHBvc3NpYmxlIHZhbHVlcyBmb3IgYW55IFBvaXNzb24gcmFuZG9tIHZhcmlhYmxlIHJhbmdlIGZyb20gJDAkIHRvICRcaW5mdHkkLCB0aGUgc3VwcG9ydCBkb2Vzbid0IGRlcGVuZCBvbiAkXGxhbWJkYSQKLSAkUChZPXkpID0gXGZyYWN7ZV57LVxsYW1iZGF9XGxhbWJkYV55fXt5IX0kCi0gc2luY2UgJFxsYW1iZGFeeSA9IGVee3lsb2coXGxhbWJkYSl9JAotICRQKFk9eSkgPSBlXnstXGxhbWJkYX1lXnt5bG9nKFxsYW1iZGEpfWVeey1sb2coeSEpfSQKLSAkUChZPXkpID0gZV57eWxvZyhcbGFtYmRhKSAtIFxsYW1iZGEgLSBsb2coeSEpfSQKLSB0aGVuLCAkYihcdGhldGEpID0gbG9nKFxsYW1iZGEpJAoKIyMgR0xNIGd1dC1jaGVjayAoQmlub21pYWwpCgojIyMgR29hbDoKClxbZih5OyBcdGhldGEpPWV4cF57KGEoeSliKFx0aGV0YSkgKyBjKFx0aGV0YSkgKyBkKHkpKX1cXQoKKipXaGF0IGlmIG91ciByZXNwb25zZSB2YXJpYWJsZSBpcy4uLioqCgojIyMgQmlub21pYWw/CgotIHN1cHBvcnQ6IGZvciBhbnkgdmFsdWUgb2YgJHAkLCAkMCA8IHAgPCAxJCwgYWxsIGludGVnZXIgdmFsdWVzIGZyb20gMCB0byAkbiQgYXJlIHBvc3NpYmxlIHNvIHRoZSBzdXBwb3J0IGRvZXNuJ3QgZGVwZW5kIG9uICRwJC4KLSAkUChZPXkpID0ge25cY2hvb3Nle3l9fXBeeSgxLXApXntuLXl9JAotICRQKFk9eSkgPSBlXnt5bG9nKHApICsgKG4teSlsb2coMS1wKSArIGxvZ3tuXGNob29zZXt5fX19JAotICRQKFk9eSkgPSBlXnt5bG9nKFxmcmFje3B9ezEtcH0pICsgbmxvZygxLXApICsgbG9ne25cY2hvb3Nle3l9fX0kCi0gdGhlbiwgJGIoXHRoZXRhKSA9IGxvZyhcZnJhY3twfXsxLXB9KSQKLSB0aGlzIGlzIGNhbGxlZCBhICoqbG9naXQqKiBmdW5jdGlvbiwgYW5kIGl0IGlzIGludGVycHJldGVkIGFzIHRoZSBsb2cgb2YgdGhlIG9kZHMgb2YgInN1Y2Nlc3MiIHdoZXJlIHRoZSBvYnNlcnZlZCBvdXRjb21lIGlzIGRlZW1lZCBlaXRoZXIgInN1Y2Nlc3MiIG9yICJmYWlsdXJlIiAKCgojIyBHTE0gZ3V0LWNoZWNrIChOb3JtYWwpCgojIyMgR29hbDoKClxbZih5OyBcdGhldGEpPWV4cF57KGEoeSliKFx0aGV0YSkgKyBjKFx0aGV0YSkgKyBkKHkpKX1cXQoKKipXaGF0IGlmIG91ciByZXNwb25zZSB2YXJpYWJsZSBpcy4uLioqCgojIyMgLi4uTm9ybWFsPwoKLSAkZih5KSA9IFxmcmFjezF9e1xzaWdtYVxzcXJ0KDJccGkpfWVeey1cZnJhY3soeSAtIFxtdSleMn17MlxzaWdtYV4yfX0kCgo+LSAkZih5KSA9IGVeey1sb2coXHNpZ21hKS1sb2coMlxwaSkvMn1lXnstXGZyYWN7KHkgLSBcbXUpXjJ9ezJcc2lnbWFeMn19JAo+LSBub3QgbG9va2luZyBnb29kIGZvciBvdXIgaGVybywgYnV0IGRvbid0IHBhbmljLi4uIHdoYXQgaWYgd2UgYXNzdW1lICRcc2lnbWEkIGlzIGtub3duIChpLmUuIGNvbnN0YW50KT8KPi0gJGYoeSkgXHByb3B0byBlXnstKC0yeVxtdSArIFxtdV4yICsgeV4yKX0kCj4tIHRoZW4sICRiKFx0aGV0YSkgPSBcbXUkCj4tIHNvLCAkXG11X3tZfFh9ID0gXGJldGFfezB9ICsgXGJldGFfezF9WCQKPi0gYW5kLCBvZiBjb3Vyc2UsIHBvc3NpYmxlIHZhbHVlcyBmb3IgYW55IE5vcm1hbCByYW5kb20gdmFyaWFibGUgcmFuZ2UgZnJvbSAkLVxpbmZ0eSQgdG8gJFxpbmZ0eSQsIHNvIHRoZSBzdXBwb3J0IGxvb2tzIGdvb2QhCj4tIERvZXMgdGhpcyBzcXVhcmUgd2l0aCB3aGF0IHlvdSBhbHJlYWR5IGtub3cgYWJvdXQgbXVsdGlwbGUgcmVncmVzc2lvbiB3aXRoIGEgTm9ybWFsIHJlc3BvbnNlPwoKIyMgV2hvJ3MgaW4gdGhlIGZhbWlseT8KCkhlcmUgYXJlIHNvbWUgb3RoZXIgZGlzdHJpYnV0aW9ucyB0aGF0IGNhbiBiZSB3cml0dGVuIGluIG9uZS1wYXJhbWV0ZXIgZXhwb25lbnRpYWwgZmFtaWx5IGZvcm06IAoKLSBCZXJub3VsbGkKLSBCaW5vbWlhbAotIFBvaXNzb24KLSBOb3JtYWwKLSBFeHBvbmVudGlhbAotIEdhbW1hCi0gR2VvbWV0cmljCi0gTmVnYXRpdmUgQmlub21pYWwKLSBhbmQgc29tZSBvdGhlcnMgdGhhdCBhcmVuJ3QgYXMgY29tbW9uCgoKIyMgUG9pc3NvbiBSZWdyZXNzaW9uCgotIE9uZS1wYWdlIHN1bW1hcnkgb2YgQllTSCBDaGFwdGVyIDQKLSBQb2lzc29uIFJlZ3Jlc3Npb24gQXNzdW1wdGlvbnMgKEwtTT1WLUk/IC4uLm5vdCBhcyBjYXRjaHkpOiAKICAgIC0gKipMaW5lYXJpdHkqKjogdGhlIGxvZyBvZiB0aGUgbWVhbiByYXRlICRsb2coXGxhbWJkYV97aX0pJCBtdXN0IGJlIGEgbGluZWFyIGZ1bmN0aW9uIG9mIHgKICAgIC0gKipNZWFuID0gVmFyaWFuY2UqKiBieSBkZWZpbml0aW9uIGJlY2F1c2UgaXQncyBQb2lzc29uCiAgICAtICoqSW5kZXBlbmRlbmNlKioKLSBJZiB0aGUgcmVzcG9uc2UgdGhyb3dzIHlvdSBhIGN1cnZlYmFsbCBsaWtlIG92ZXJkaXNwZXJzaW9uIG9yIHRvbyBtYW55IHplcm9lcyB0aGVyZSBhcmUgc2Vuc2libGUgYWRqdXN0bWVudHMgYXZhaWxhYmxlCiAgICAtIEFjY291bnRpbmcgZm9yIG92ZXJkaXNwZXJzaW9uIChCWVNILCBwLiA4MCkKICAgIC0gWmVyby1pbmZsYXRlZCBQb2lzc29uIChaSVApIG1vZGVsIChCWVNILCBwLiA4NCkKLSBpbiBSOiAKICAgIC0gYGdsbShZIH4gWCwgZmFtaWx5ID0gcG9pc3NvbilgCiAgICAtIG5vdGU6ICoqZ2xtKiogZnVuY3Rpb24gYW5kIG5vdCB0aGUgb2xkICoqbG0qKgotIGludGVycHJldGluZyBjb2VmZmljaWVudCAkXGJldGFfezF9JCAKICAgIC0gc2luY2UgJGxvZyhcbGFtYmRhX3tYfSkgPSBcYmV0YV97MH0gKyBcYmV0YV97MX1YJAogICAgLSB0aGVuLCAkZV57XGJldGFfezF9WH0gPSBcZnJhY3tcbGFtYmRhX3tYICsgMX19e1xsYW1iZGFfe1h9fSQgKHRoaXMgaXMgY2FsbGVkIGEgInJhdGUgcmF0aW8iIG9yIHNvbWV0aW1lcyAicmVsYXRpdmUgcmlzayIpCiAgICAtIGdlbmVyaWNhbGx5LCB0aGUgbWVhbiByZXNwb25zZSBjaGFuZ2VzIGJ5IGEgZmFjdG9yIG9mICRlXntcYmV0YV97MX19JCB3aXRoIGVhY2ggdW5pdCBpbmNyZWFzZSBpbiBYLiAgRm9yIGV4YW1wbGUsIGlmICRlXntcYmV0YV97MX19ID0gMC44NyQgeW91IGRlc2NyaWJlIGl0IGFzIGEgMTMlIGRlY3JlYXNlLCBhbmQgaWYgJGVee1xiZXRhX3sxfX0gPSA1LjMkIHRoZSBtZWFuIHJlc3BvbnNlIGlzIDUuMyB0aW1lcyAoY3J1ZGVseSwgNTMwJSkgaGlnaGVyLgoKIyMgTG9naXN0aWMgUmVncmVzc2lvbgoKLSBPbmUtcGFnZSBzdW1tYXJ5IG9mIEJZU0ggQ2hhcHRlciA2Ci0gQmlub21pYWwgUmVncmVzc2lvbiBBc3N1bXB0aW9uczogCiAgICAtIEJpbm9taWFsIHJlc3BvbnNlIGlzIGEgc3VtIG9mICRuJCBCZXJub3VsbGkgdHJpYWxzCiAgICAtIFRyaWFscyBhcmUgaW5kZXBlbmRlbnQKICAgIC0gJG4kIGlzIGZpeGVkIChub3RlOiAkbiA9IDEkIGlzIGZpbmUpCiAgICAtICRwJCBpcyB0aGUgc2FtZSBmb3IgYWxsIHRyaWFscwotIE92ZXJkaXNwZXJzaW9uIGFkanVzdG1lbnQgKEJZU0gsIHAuIDExNCkKLSBpbiBSOiAKICAgIC0gYGdsbShZIH4gWCwgZmFtaWx5ID0gYmlub21pYWwpYAogICAgLSBub3RlOiAqKmdsbSoqIGZ1bmN0aW9uIGFuZCBub3QgdGhlIG9sZCAqKmxtKioKLSBpbnRlcnByZXRpbmcgY29lZmZpY2llbnQgJFxiZXRhX3sxfSQgCiAgICAtIHNpbmNlICRsb2coXGZyYWN7cH17MS1wfSkgPSBcYmV0YV97MH0gKyBcYmV0YV97MX1YJAogICAgLSB0aGVuLCAkZV57XGJldGFfezF9fSA9IFxmcmFje3BfezF9LygxLXBfezF9KX17cF97MH0vKDEtcF97MH0pfSQgKHRoaXMgaXMgYW4gb2RkcyByYXRpbykKICAgIC0gYWxzbywgJHAgPSBcZnJhY3tlXntcYmV0YV97MH19ICsgZV57XGJldGFfezF9WH19ezEgKyBlXntcYmV0YV97MH19ICsgZV57XGJldGFfezF9WH19JCAgKHRoYXQncyB0aGUgcHJvYmFiaWxpdHkgb2YgInN1Y2Nlc3MiIGdpdmVuIFgpCiAgICAtIGdlbmVyaWNhbGx5LCB0aGUgb2RkcyBvZiAic3VjY2VzcyIgY2hhbmdlIGJ5IGEgZmFjdG9yIG9mICRlXntcYmV0YV97MX19JCB3aXRoIGVhY2ggdW5pdCBpbmNyZWFzZSBpbiBYLiAgRm9yIGV4YW1wbGUsIGlmICRlXntcYmV0YV97MX19ID0gMC4xMCQgdGhlIG9kZHMgaGF2ZSBkZWNyZWFzZWQgYnkgOTAlLCBhbmQgaWYgJGVee1xiZXRhX3sxfX0gPSAxMC4wJCB0aGUgb2RkcyBvZiAic3VjY2VzcyIgYXJlIDEwIHRpbWVzIChjcnVkZWx5LCAxMDAwJSkgaGlnaGVyLgoKCiMjIE9uZSBsYXN0IHRpbWUuLi4gQmFjayB0byBDaHJpcwoKIVtdKGNocmlzLnBuZykKCi0gV2hhdCBpZiB3ZSBkZWNpZGUgdG8gYXBwcm9hY2ggdGhlIHNsaWdodCBkZWxheSBkaWZmZXJlbnRseS4uLgotIE1heWJlIHdoYXQncyByZWFsbHkgaW1wb3J0YW50IHRvIENocmlzIGlzIHdoZXRoZXIgb3Igbm90IHRoZSBmbGlnaHQgKmRlcGFydHMqIG9uIHRpbWUgKHdpdGhpbiAxMCBtaW51dGVzKQogICAgLSBJZiB0aGUgcGxhbmUgKmxlYXZlcyogb24tdGltZSBhbmQgdGhlbiBhcnJpdmFsIGlzIGRlbGF5ZWQgYnkgc29tZSBwcm9ibGVtIGF0IHRoZSBkZXN0aW5hdGlvbiwgcGVyaGFwcyB0aGUgY2xpZW50IHdvdWxkIGJlIHVuZGVyc3RhbmRpbmcuCiAgICAtIFJlc3BvbnNlOiBgdGltZWx5RGVwYXJ0YCBjb2RlZCB7VFJVRTsgRkFMU0V9IGlzICoqYmluYXJ5KioKICAgIC0gRXhwbGFuYXRvcnkgVmFyaWFibGVzOiBgaG91cmAgb2YgdGhlIGRheTsgYGRvd2AgZGF5IG9mIHRoZSB3ZWVrCgoKYGBge3J9CnJlcXVpcmUobHVicmlkYXRlKQojIHNvbWUgbWlub3Igd3JhbmdsaW5nICh3aGF0IGFyZSB3ZSBkb2luZyBoZXJlPykKT3JkQ29sbGVhZ3VlRGF0YSA8LSAKICBPcmRDb2xsZWFndWVEYXRhICU+JQogIG11dGF0ZSh0aW1lbHlEZXBhcnQgPSBpZl9lbHNlKGRlcF9kZWxheSA8IDEwLCBUUlVFLCBGQUxTRSksIAogICAgICAgICBkYXRlID0geW1kKHBhc3RlMCh5ZWFyLCAiLSIsIG1vbnRoLCAiLSIsIGRheSkpLAogICAgICAgICBkb3cgPSBhcy5jaGFyYWN0ZXIod2RheShkYXRlLCBsYWJlbCA9IFRSVUUpKSkKCmBgYAoKIyMgUGxvdCB0aGUgcmVsYXRpb25zaGlwCgotIExldCdzIGxvb2sgYXQgb24tdGltZSBkZXBhcnR1cmUgc3RhdHVzIGJ5IGhvdXIgb2YgdGhlIGRheSBmb3Igb3VyIGRhdGEKCmBgYHtyfQpPcmRDb2xsZWFndWVEYXRhICU+JQogIGZpbHRlcighaXMubmEodGltZWx5RGVwYXJ0KSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gaG91ciwgeSA9IGFzLm51bWVyaWModGltZWx5RGVwYXJ0KSkpICsgCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjMsIGhlaWdodCA9IDAuMDUpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImdsbSIsIG1ldGhvZC5hcmdzID0gbGlzdChmYW1pbHkgPSAiYmlub21pYWwiKSwgc2UgPSAwKSArCiAgeWxhYigiT24tVGltZSBEZXBhcnR1cmUgU3RhdHVzIikKYGBgCgoKIyMgTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbGluZwoKLSBBZ2Fpbiwgd2Ugc2hvdWxkIHdyaXRlIGRvd24gdGhlIG1vZGVsIHRvIGJlIHN1cmUgd2UgdW5kZXJzdGFuZCBpdC4uLgotIG5vdGUgYWJvdXQgZGlzcGVyc2lvbiBhbmQgInF1YXNpYmlub21pYWwiCgpgYGB7cn0KbG9naXRNb2QgPC0gZ2xtKHRpbWVseURlcGFydCB+IGhvdXIgKyBkb3csIGZhbWlseSA9ICJiaW5vbWlhbCIsIAogICAgICAgICAgICAgICAgZGF0YSA9IE9yZENvbGxlYWd1ZURhdGEpCm1zdW1tYXJ5KGxvZ2l0TW9kKQpjb25maW50KGxvZ2l0TW9kKQpgYGAKCgoKIyMgT3RoZXIgbW9kZWwgZmFtaWxpZXMKCi0gT25jZSB5b3XigJl2ZSBtYXN0ZXJlZCBsaW5lYXIgbW9kZWxzLCBpdCdzIGVhc3kgdG8gbGVhcm4gdGhlIG1lY2hhbmljcyBvZiBvdGhlciBtb2RlbCBjbGFzc2VzOiAKICAgIC0gKipHZW5lcmFsaXNlZCBsaW5lYXIgbW9kZWxzKiogKEdMTTsgYHN0YXRzOjpnbG0oKWApOiB1c2UgYSBzdGF0aXN0aWNhbCBpZGVhIGNhbGxlZCAibGlrZWxpaG9vZCIgdG8gYWNjb21tb2RhdGUgZWl0aGVyIGEgY29udGludW91cyAqKm9yIG5vbi1jb250aW51b3VzIHJlc3BvbnNlIHZhcmlhYmxlKiogKGUuZy4gYmluYXJ5LCBiaW5vbWlhbCwgb3IgY291bnRzKQogICAgLSAqKkdlbmVyYWxpc2VkIGFkZGl0aXZlIG1vZGVscyoqIChHQU07IGBtZ2N2OjpnYW0oKWApOiBleHRlbmQgR0xNIHRvIGluY29ycG9yYXRlIGFyYml0cmFyeSBzbW9vdGggZnVuY3Rpb25zCiAgICAtIFBlbmFsaXplZCBsaW5lYXIgbW9kZWxzIChlLmcuLCBMYXNzbzsgYGdsbW5ldDo6Z2xtbmV0KClgKTogYWRkIGEgcGVuYWx0eSB0ZXJtIHRvIHRoZSBkaXN0YW5jZSBtZXRyaWMgaW4gcm9kZXIgdG8gcmVkdWNlIG1vZGVsIGNvbXBsZXhpdHkgaW4gYW4gYXR0ZW1wdCB0byBwcm9kdWNlIG1vZGVscyB0aGF0IGdlbmVyYWxpemUgYmV0dGVyIHRvIG5ldyAob3V0LW9mLXNhbXBsZSkgZGF0YXNldHMgZnJvbSB0aGUgc2FtZSBwb3B1bGF0aW9uCiAgICAtIFJvYnVzdCBsaW5lYXIgbW9kZWxzIChgTUFTUzo6cmxtKClgKTogdHdlYWsgdGhlIGRpc3RhbmNlIG1ldHJpYyB0byBiZSBsZXNzIHNlbnNpdGl2ZSB0byBvdXRsaWVycywgYXQgdGhlIGNvc3Qgb2YgYmVpbmcgbm90IHF1aXRlIGFzIGVmZmljaWVudCB3aGVuIHRoZXJlIGFyZSBubyBvdXRsaWVycy4KICAgIC0gVHJlZXMgKGUuZy4gQ0FSVDsgYHJwYXJ0OjpycGFydCgpYCk6IHVzZXMgcmVjdXJzaXZlIHBhcnRpdGlvbmluZyB0byBmaXQgYSBwaWVjZS13aXNlIGNvbnN0YW50IG1vZGVsLCBzcGxpdHRpbmcgdGhlIGRhdGEgaW50byBwcm9ncmVzc2l2ZWx5IHNtYWxsZXIgYW5kIHNtYWxsZXIgcGllY2VzLiAgCiAgICAgICAgLSBNb3N0IGVmZmVjdGl2ZSB3aGVuIHVzZWQgaW4gYWdncmVnYXRlCiAgICAgICAgLSByYW5kb20gZm9yZXN0cyAoYHJhbmRvbUZvcmVzdDo6cmFuZG9tRm9yZXN0KClgKQogICAgICAgIC0gZ3JhZGllbnQgYm9vc3RpbmcgbWFjaGluZXMgKGB4Z2Jvb3N0Ojp4Z2Jvb3N0KClgKQoKCg==